From 6927aab9f916734f998d807fb7f199922fcb868c Mon Sep 17 00:00:00 2001 From: Chubbygummibear <46236974+Chubbygummibear@users.noreply.github.com> Date: Wed, 13 Jul 2022 23:49:08 -0700 Subject: [PATCH 1/4] please let this work (#1) * ho * pod nerfed by a pod main * sugone * yea * yer * lotsa stuff * tgui is pain * i don't know * maybe? * god kill me * tailed * wait no i fixed it wrong, this should be better * SCOPE CREEP BABEEEEEEEEEE * sins * mother of all omlettes jack * is it over? * ethereal colors because idk * i think that's it until we find out what else i broke cl ass clean up debug messages last one probably * Revert "i think that's it until we find out what else i broke" This reverts commit 574b0e8b75c0a1f5b6361d225e33344eee6e9016. * last time with gusto * hhh * ho * pod nerfed by a pod main * yer * fix * Revert "fix" This reverts commit 979eddbaa8889a249cc9ff830109757af888e753. * Delete plantpeople.dm * Revert "Merge branch 'dna-blocks-but-like-good' of https://github.com/Chubbygummibear/Yogstation-TG into dna-blocks-but-like-good" This reverts commit 1756ff2be65686ea062193fc14ce1ed34ef1988f, reversing changes made to 05f88bee81b29e3e26140d0e9235d9f2784bbff1. * this shouldn't be in here * missed that * so many little things * please for the love of god work --- code/__DEFINES/DNA.dm | 27 ++ code/__HELPERS/dna.dm | 7 +- code/__HELPERS/mobs.dm | 22 +- code/__HELPERS/sanitize_values.dm | 54 ++- code/_globalvars/lists/flavor_misc.dm | 21 +- code/datums/components/forensics.dm | 2 +- code/datums/datacore.dm | 4 +- code/datums/diseases/retrovirus.dm | 22 +- code/datums/dna.dm | 349 ++++++++++++++---- code/datums/mutations/actions.dm | 2 +- code/datums/mutations/body.dm | 13 +- .../weather/weather_types/radiation_storm.dm | 7 +- code/game/machinery/cloning.dm | 4 +- code/game/machinery/computer/cloning.dm | 4 +- code/game/machinery/computer/dna_console.dm | 110 +++++- code/game/machinery/exp_cloner.dm | 6 +- code/game/objects/items/cosmetics.dm | 2 +- code/game/objects/items/dna_injector.dm | 19 +- code/game/objects/structures/mirror.dm | 23 +- code/modules/admin/create_mob.dm | 4 +- code/modules/admin/secrets.dm | 2 +- .../antagonists/abductor/equipment/gland.dm | 1 + .../antagonists/bloodsuckers/powers/veil.dm | 2 +- .../eldritch_cult/eldritch_transmutations.dm | 4 +- .../antagonists/wizard/equipment/artefact.dm | 2 +- code/modules/client/preferences.dm | 50 +-- code/modules/client/preferences_savefile.dm | 23 +- code/modules/detectivework/scanner.dm | 2 +- code/modules/events/disease_outbreak.dm | 2 +- code/modules/events/spacevine.dm | 4 +- code/modules/goals/station_goals/dna_vault.dm | 4 +- code/modules/mob/dead/observer/observer.dm | 4 +- code/modules/mob/living/carbon/human/human.dm | 2 +- .../mob/living/carbon/human/species.dm | 56 +-- .../carbon/human/species_types/eggpeople.dm | 2 +- .../carbon/human/species_types/ethereal.dm | 4 +- .../carbon/human/species_types/felinid.dm | 8 +- .../carbon/human/species_types/golems.dm | 44 +-- .../carbon/human/species_types/jellypeople.dm | 1 + .../human/species_types/lizardpeople.dm | 2 +- .../carbon/human/species_types/mushpeople.dm | 4 +- .../carbon/human/species_types/podpeople.dm | 7 +- .../carbon/human/species_types/polysmorphs.dm | 2 +- .../mob/living/carbon/human/update_icons.dm | 2 +- code/modules/mob/living/carbon/life.dm | 9 +- code/modules/mob/living/carbon/monkey/life.dm | 2 +- code/modules/projectiles/projectile/magic.dm | 5 +- .../chemistry/reagents/other_reagents.dm | 3 +- .../chemistry/reagents/toxin_reagents.dm | 7 +- .../crossbreeding/_status_effects.dm | 1 + .../research/xenobiology/xenobiology.dm | 1 + .../ruins/icemoonruin_code/hotsprings.dm | 1 + code/modules/surgery/bodyparts/_bodyparts.dm | 6 +- code/modules/surgery/bodyparts/head.dm | 10 +- code/modules/surgery/bodyparts/helpers.dm | 26 +- code/modules/surgery/organs/tails.dm | 47 ++- tgui/packages/tgui/interfaces/DnaConsole.js | 67 +++- .../guardian/abilities/major/predator.dm | 2 +- .../carbon/human/species_types/plantpeople.dm | 8 +- 59 files changed, 789 insertions(+), 342 deletions(-) diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index a3ed9e72d5e1..a67f371fa271 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -59,6 +59,7 @@ #define UI_CHANGED "ui changed" #define UE_CHANGED "ue changed" +#define UF_CHANGED "uf changed" #define CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY 204 @@ -78,6 +79,11 @@ //DNA - Because fuck you and your magic numbers being all over the codebase. #define DNA_BLOCK_SIZE 3 +//this should be in a colors.dm define but we don't use that like tg does +#define DEFAULT_HEX_COLOR_LEN 6 + +#define DNA_BLOCK_SIZE_COLOR DEFAULT_HEX_COLOR_LEN + #define DNA_UNI_IDENTITY_BLOCKS 7 #define DNA_HAIR_COLOR_BLOCK 1 #define DNA_FACIAL_HAIR_COLOR_BLOCK 2 @@ -87,6 +93,27 @@ #define DNA_FACIAL_HAIR_STYLE_BLOCK 6 #define DNA_HAIR_STYLE_BLOCK 7 +#define DNA_FEATURE_BLOCKS 19 +#define DNA_MUTANT_COLOR_BLOCK 1 +#define DNA_ETHEREAL_COLOR_BLOCK 2 +#define DNA_LIZARD_MARKINGS_BLOCK 3 +#define DNA_LIZARD_TAIL_BLOCK 4 +#define DNA_SNOUT_BLOCK 5 +#define DNA_HORNS_BLOCK 6 +#define DNA_FRILLS_BLOCK 7 +#define DNA_SPINES_BLOCK 8 +#define DNA_HUMAN_TAIL_BLOCK 9 +#define DNA_EARS_BLOCK 10 +#define DNA_MOTH_WINGS_BLOCK 11 +#define DNA_MUSHROOM_CAPS_BLOCK 12 +#define DNA_POLY_TAIL_BLOCK 13 +#define DNA_POLY_TEETH_BLOCK 14 +#define DNA_POLY_DOME_BLOCK 15 +#define DNA_POLY_DORSAL_BLOCK 16 +#define DNA_ETHEREAL_MARK_BLOCK 17 +#define DNA_PLANT_HAIR_BLOCK 18 +#define DNA_PLANT_FLOWER_BLOCK 19 + #define DNA_SEQUENCE_LENGTH 4 #define DNA_MUTATION_BLOCKS 8 #define DNA_UNIQUE_ENZYMES_LEN 32 diff --git a/code/__HELPERS/dna.dm b/code/__HELPERS/dna.dm index 5ec66923ea54..0ec3e7ee8d49 100644 --- a/code/__HELPERS/dna.dm +++ b/code/__HELPERS/dna.dm @@ -10,4 +10,9 @@ #define GET_MUTATION_STABILIZER(A) ((A.stabilizer_coeff < 0) ? 1 : A.stabilizer_coeff) #define GET_MUTATION_SYNCHRONIZER(A) ((A.synchronizer_coeff < 0) ? 1 : A.synchronizer_coeff) #define GET_MUTATION_POWER(A) ((A.power_coeff < 0) ? 1 : A.power_coeff) -#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff) \ No newline at end of file +#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff) + +///Getter macro used to get the length of a identity block +#define GET_UI_BLOCK_LEN(blocknum) (GLOB.identity_block_lengths["[blocknum]"] || DNA_BLOCK_SIZE) +///Ditto, but for a feature. +#define GET_UF_BLOCK_LEN(blocknum) (GLOB.features_block_lengths["[blocknum]"] || DNA_BLOCK_SIZE) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index a1adb0d3f60b..2c2781af4b13 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -4,21 +4,21 @@ /proc/random_eye_color() switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino")) if("brown") - return "630" + return "#663300" if("hazel") - return "542" + return "#554422" if("grey") - return pick("666","777","888","999","aaa","bbb","ccc") + return pick("#666666","#777777","#888888","#999999","#aaaaaa","#bbbbbb","#cccccc") if("blue") - return "36c" + return "#3366cc" if("green") - return "060" + return "#006600" if("amber") - return "fc0" + return "#ffcc00" if("albino") - return pick("c","d","e","f") + pick("0","1","2","3","4","5","6","7","8","9") + pick("0","1","2","3","4","5","6","7","8","9") + return "#" + pick("cc","dd","ee","ff") + pick("00","11","22","33","44","55","66","77","88","99") + pick("00","11","22","33","44","55","66","77","88","99") else - return "000" + return "#000000" /proc/random_underwear(gender) if(!GLOB.underwear_list.len) @@ -87,9 +87,9 @@ //For now we will always return none for tail_human and ears. this shit was unreadable if you do somethign like this make it at least readable return(list( - "mcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "mcolor" = "#[pick("7F","FF")][pick("7F","FF")][pick("7F","FF")]", "gradientstyle" = random_hair_gradient_style(10), - "gradientcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "gradientcolor" = "#[pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")]", "ethcolor" = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)], "tail_lizard" = pick(GLOB.tails_list_lizard), "tail_human" = "None", @@ -103,7 +103,7 @@ "legs" = "Normal Legs", "caps" = pick(GLOB.caps_list), "moth_wings" = pick(GLOB.moth_wings_list), - "tail_polysmorph" = "Polys", + "tail_polysmorph" = pick(GLOB.tails_list_polysmorph), "teeth" = pick(GLOB.teeth_list), "dome" = pick(GLOB.dome_list), "dorsal_tubes" = pick(GLOB.dorsal_tubes_list), diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index 783b0463caa3..97ce3664f7f8 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -6,17 +6,30 @@ return number return default +/proc/sanitize_float(number, min=0, max=1, accuracy=1, default=0) + if(isnum(number)) + number = round(number, accuracy) + if(min <= number && number <= max) + return number + return default + /proc/sanitize_text(text, default="") if(istext(text)) return text return default +/proc/sanitize_islist(value, default) + if(islist(value) && length(value)) + return value + if(default) + return default + /proc/sanitize_inlist(value, list/List, default) if(value in List) return value if(default) return default - if(List && List.len) + if(List?.len) return pick(List) @@ -38,7 +51,7 @@ return default return default -/proc/sanitize_hexcolor(color, desired_format=3, include_crunch=0, default) +/proc/sanitize_hexcolor(color, desired_format = DEFAULT_HEX_COLOR_LEN, include_crunch = TRUE, default) var/crunch = include_crunch ? "#" : "" if(!istext(color)) color = "" @@ -46,32 +59,39 @@ var/start = 1 + (text2ascii(color, 1) == 35) var/len = length(color) var/char = "" - // RRGGBB -> RGB but awful - var/convert_to_shorthand = desired_format == 3 && length_char(color) > 3 + // Used for conversion between RGBA hex formats. + var/format_input_ratio = "[desired_format]:[length_char(color)-(start-1)]" . = "" var/i = start while(i <= len) char = color[i] + i += length(char) switch(text2ascii(char)) - if(48 to 57) //numbers 0 to 9 + if(48 to 57) //numbers 0 to 9 + . += char + if(97 to 102) //letters a to f . += char - if(97 to 102) //letters a to f + if(65 to 70) //letters A to F + char = lowertext(char) . += char - if(65 to 70) //letters A to F - . += lowertext(char) else break - i += length(char) - if(convert_to_shorthand && i <= len) //skip next one - i += length(color[i]) - - if(length_char(.) != desired_format) - if(default) - return default - return crunch + repeat_string(desired_format, "0") + switch(format_input_ratio) + if("3:8", "4:8", "3:6", "4:6") //skip next one. RRGGBB(AA) -> RGB(A) + i += length(color[i]) + if("6:4", "6:3", "8:4", "8:3") //add current char again. RGB(A) -> RRGGBB(AA) + . += char - return crunch + . + if(length_char(.) == desired_format) + return crunch + . + switch(format_input_ratio) //add or remove alpha channel depending on desired format. + if("3:8", "3:4", "6:4") + return crunch + copytext(., 1, desired_format+1) + if("4:6", "4:3", "8:3") + return crunch + . + ((desired_format == 4) ? "f" : "ff") + else //not a supported hex color format. + return default ? default : crunch + repeat_string(desired_format, "0") /proc/sanitize_ooccolor(color) if(length(color) != length_char(color)) diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 21b43fc86081..c3c89640f770 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -44,7 +44,26 @@ GLOBAL_LIST_EMPTY(moth_wingsopen_list) GLOBAL_LIST_EMPTY(caps_list) GLOBAL_LIST_EMPTY(ethereal_mark_list) //ethereal face marks -GLOBAL_LIST_INIT(color_list_ethereal, list("F Class(Green)" = "97ee63", "F2 Class (Light Green)" = "00fa9a", "F3 Class (Dark Green)" = "37835b", "M Class (Red)" = "9c3030", "M1 Class (Purple)" = "ee82ee", "G Class (Yellow)" = "fbdf56", "O Class (Blue)" = "3399ff", "A Class (Cyan)" = "00ffff")) +GLOBAL_LIST_INIT(color_list_ethereal, list( + "F Class (Green)" = "#97ee63", + "F2 Class (Light Green)" = "#00fa9a", + "F3 Class (Dark Green)" = "#37835b", + "F4 Class (Faint Green)" = "#ddff99", + "M Class (Red)" = "#9c3030", + "M1 Class (Purple)" = "#ee82ee", + "M2 Class (Faint Red)" = "#ff4d4d", + "M3 Class (Light Pink)" = "#ffb3b3", + "M4 Class (Pink)" = "#ff99cc", + "G Class (Yellow)" = "#fbdf56", + "G1 Class (Bright Yellow)" = "#ffff99", + "O Class (Blue)" = "#3399ff", + "O1 Class (Dark Blue)" = "#6666ff", + "O2 Class (Faint Blue)" = "#b3d9ff", + "O3 Class (Dark Fuschia)" = "#cc0066", + "A Class (Cyan)" = "#00ffff", + "K Class (Orange)" = "#ffa64d", + "K1 Class (Burnt Orange)" = "#cc4400", + "White Dwarf" = "#f2f2f2",)) GLOBAL_LIST_EMPTY(pod_hair_list) //ethereal face marks GLOBAL_LIST_EMPTY(pod_flower_list) //ethereal face marks diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm index 9cdca54d9c9b..1695f44237a2 100644 --- a/code/datums/components/forensics.dm +++ b/code/datums/components/forensics.dm @@ -90,7 +90,7 @@ if(!ignoregloves) H.gloves.add_fingerprint(H, TRUE) //ignoregloves = 1 to avoid infinite loop. return - var/full_print = md5(H.dna.uni_identity) + var/full_print = md5(H.dna.unique_identity) LAZYSET(fingerprints, full_print, full_print) return TRUE diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index e67af876d9eb..3b22088beb81 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -326,7 +326,7 @@ G.fields["rank"] = assignment G.fields["age"] = H.age G.fields["species"] = H.dna.species.name - G.fields["fingerprint"] = md5(H.dna.uni_identity) + G.fields["fingerprint"] = md5(H.dna.unique_identity) G.fields["p_stat"] = "Active" G.fields["m_stat"] = "Stable" G.fields["gender"] = H.gender @@ -383,7 +383,7 @@ G.fields["gender"] = "Other" L.fields["blood_type"] = H.dna.blood_type L.fields["b_dna"] = H.dna.unique_enzymes - L.fields["identity"] = H.dna.uni_identity + L.fields["identity"] = H.dna.unique_identity L.fields["species"] = H.dna.species.type L.fields["features"] = H.dna.features L.fields["image"] = image diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm index 81cb105cda5e..c54724705a37 100644 --- a/code/datums/diseases/retrovirus.dm +++ b/code/datums/diseases/retrovirus.dm @@ -66,10 +66,13 @@ to_chat(affected_mob, span_danger("Your entire body vibrates.")) if (prob(35)) - if(prob(50)) - scramble_dna(affected_mob, 1, 0, rand(15,45)) - else - scramble_dna(affected_mob, 0, 1, rand(15,45)) + switch(rand(1,3)) + if(1) + scramble_dna(affected_mob, 1, 0, 0, rand(15,45)) + if(2) + scramble_dna(affected_mob, 0, 1, 0, rand(15,45)) + if(3) + scramble_dna(affected_mob, 0, 0, 1, rand(15,45)) if(4) if(restcure) @@ -78,7 +81,10 @@ cure() return if (prob(60)) - if(prob(50)) - scramble_dna(affected_mob, 1, 0, rand(50,75)) - else - scramble_dna(affected_mob, 0, 1, rand(50,75)) \ No newline at end of file + switch(rand(1,3)) + if(1) + scramble_dna(affected_mob, 1, 0, 0, rand(50,75)) + if(2) + scramble_dna(affected_mob, 0, 1, 0, rand(50,75)) + if(3) + scramble_dna(affected_mob, 0, 0, 1, rand(50,75)) diff --git a/code/datums/dna.dm b/code/datums/dna.dm index f551fd96b61d..dbd3e4d94017 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -1,11 +1,62 @@ +/** + * Some identity blocks (basically pieces of the unique_identity string variable of the dna datum, commonly abbreviated with ui) + * may have a length that differ from standard length of 3 ASCII characters. This list is necessary + * for these non-standard blocks to work, as well as the entire unique identity string. + * Should you add a new ui block which size differ from the standard (again, 3 ASCII characters), like for example, a color, + * please do not forget to also include it in this list in the following format: + * "[dna block number]" = dna block size, + * Failure to do that may result in bugs. Thanks. + */ +GLOBAL_LIST_INIT(identity_block_lengths, list( + "[DNA_HAIR_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + "[DNA_FACIAL_HAIR_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + "[DNA_EYE_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + )) + +/** + * The same rules of the above also apply here, with the exception that this is for the unique_features string variable + * (commonly abbreviated with uf) and its blocks. Both ui and uf have a standard block length of 3 ASCII characters. + */ +GLOBAL_LIST_INIT(features_block_lengths, list( + "[DNA_MUTANT_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + "[DNA_ETHEREAL_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + )) + +/** + * A list of numbers that keeps track of where ui blocks start in the unique_identity string variable of the dna datum. + * Commonly used by the datum/dna/set_uni_identity_block and datum/dna/get_uni_identity_block procs. + */ +GLOBAL_LIST_INIT(total_ui_len_by_block, populate_total_ui_len_by_block()) + +/proc/populate_total_ui_len_by_block() + . = list() + var/total_block_len = 1 + for(var/blocknumber in 1 to DNA_UNI_IDENTITY_BLOCKS) + . += total_block_len + total_block_len += GET_UI_BLOCK_LEN(blocknumber) + +///Ditto but for unique features. Used by the datum/dna/set_uni_feature_block and datum/dna/get_uni_feature_block procs. +GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) + +/proc/populate_total_uf_len_by_block() + . = list() + var/total_block_len = 1 + for(var/blocknumber in 1 to DNA_FEATURE_BLOCKS) + . += total_block_len + total_block_len += GET_UF_BLOCK_LEN(blocknumber) + /////////////////////////// DNA DATUM /datum/dna + ///An md5 hash of the dna holder's real name var/unique_enzymes - var/uni_identity + ///Stores the hashed values of traits such as skin tones, hair style, and gender + var/unique_identity var/blood_type var/datum/species/species = new /datum/species/human //The type of mutant race the player is if applicable (i.e. potato-man) var/list/features = list("FFF") //first value is mutant color + ///Stores the hashed values of the person's non-human features + var/unique_features var/real_name //Stores the real name of the person who originally got this dna datum. Used primarely for changelings, var/list/mutations = list() //All mutations are from now on here var/list/temporary_mutations = list() //Temporary changes to the UE @@ -41,9 +92,10 @@ if(!istype(destination)) return destination.dna.unique_enzymes = unique_enzymes - destination.dna.uni_identity = uni_identity + destination.dna.unique_identity = unique_identity destination.dna.blood_type = blood_type destination.set_species(species.type, icon_update=0) + destination.dna.unique_features = unique_features destination.dna.features = features.Copy() destination.dna.real_name = real_name destination.dna.temporary_mutations = temporary_mutations.Copy() @@ -56,7 +108,8 @@ new_dna.unique_enzymes = unique_enzymes new_dna.mutation_index = mutation_index new_dna.default_mutation_genes = default_mutation_genes - new_dna.uni_identity = uni_identity + new_dna.unique_identity = unique_identity + new_dna.unique_features = unique_features new_dna.blood_type = blood_type new_dna.features = features.Copy() new_dna.species = new species.type @@ -90,7 +143,7 @@ if((HM.class in classes) && !(HM.mutadone_proof && mutadone)) force_lose(HM) -/datum/dna/proc/generate_uni_identity() +/datum/dna/proc/generate_unique_identity() . = "" var/list/L = new /list(DNA_UNI_IDENTITY_BLOCKS) @@ -106,20 +159,63 @@ if(!GLOB.hair_styles_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/hair,GLOB.hair_styles_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list) L[DNA_HAIR_STYLE_BLOCK] = construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len) - L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color) + L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color, include_crunch = FALSE) if(!GLOB.facial_hair_styles_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) L[DNA_FACIAL_HAIR_STYLE_BLOCK] = construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len) - L[DNA_FACIAL_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.facial_hair_color) + L[DNA_FACIAL_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.facial_hair_color, include_crunch = FALSE) L[DNA_SKIN_TONE_BLOCK] = construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len) - L[DNA_EYE_COLOR_BLOCK] = sanitize_hexcolor(H.eye_color) + L[DNA_EYE_COLOR_BLOCK] = sanitize_hexcolor(H.eye_color, include_crunch = FALSE) - for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++) - if(L[i]) - . += L[i] - else - . += random_string(DNA_BLOCK_SIZE,GLOB.hex_characters) - return . + for(var/blocknum in 1 to DNA_UNI_IDENTITY_BLOCKS) + . += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters) + +/datum/dna/proc/generate_unique_features() + . = "" + + var/list/L = new /list(DNA_FEATURE_BLOCKS) + + if(features["mcolor"]) + L[DNA_MUTANT_COLOR_BLOCK] = sanitize_hexcolor(features["mcolor"], include_crunch = FALSE) + if(features["ethcolor"]) + L[DNA_ETHEREAL_COLOR_BLOCK] = sanitize_hexcolor(features["ethcolor"], include_crunch = FALSE) + if(features["body_markings"]) + L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len) + if(features["tail_lizard"]) + L[DNA_LIZARD_TAIL_BLOCK] = construct_block(GLOB.tails_list_lizard.Find(features["tail_lizard"]), GLOB.tails_list_lizard.len) + if(features["snout"]) + L[DNA_SNOUT_BLOCK] = construct_block(GLOB.snouts_list.Find(features["snout"]), GLOB.snouts_list.len) + if(features["horns"]) + L[DNA_HORNS_BLOCK] = construct_block(GLOB.horns_list.Find(features["horns"]), GLOB.horns_list.len) + if(features["frills"]) + L[DNA_FRILLS_BLOCK] = construct_block(GLOB.frills_list.Find(features["frills"]), GLOB.frills_list.len) + if(features["spines"]) + L[DNA_SPINES_BLOCK] = construct_block(GLOB.spines_list.Find(features["spines"]), GLOB.spines_list.len) + if(features["tail_human"]) + L[DNA_HUMAN_TAIL_BLOCK] = construct_block(GLOB.tails_list_human.Find(features["tail_human"]), GLOB.tails_list_human.len) + if(features["ears"]) + L[DNA_EARS_BLOCK] = construct_block(GLOB.ears_list.Find(features["ears"]), GLOB.ears_list.len) + if(features["moth_wings"] != "Burnt Off") + L[DNA_MOTH_WINGS_BLOCK] = construct_block(GLOB.moth_wings_list.Find(features["moth_wings"]), GLOB.moth_wings_list.len) + if(features["caps"]) + L[DNA_MUSHROOM_CAPS_BLOCK] = construct_block(GLOB.caps_list.Find(features["caps"]), GLOB.caps_list.len) + if(features["tail_polysmorph"]) + L[DNA_POLY_TAIL_BLOCK] = construct_block(GLOB.tails_list_polysmorph.Find(features["tail_polysmorph"]), GLOB.tails_list_polysmorph.len) + if(features["teeth"]) + L[DNA_POLY_TEETH_BLOCK] = construct_block(GLOB.teeth_list.Find(features["teeth"]), GLOB.teeth_list.len) + if(features["dome"]) + L[DNA_POLY_DOME_BLOCK] = construct_block(GLOB.dome_list.Find(features["dome"]), GLOB.dome_list.len) + if(features["dorsal_tubes"]) + L[DNA_POLY_DORSAL_BLOCK] = construct_block(GLOB.dorsal_tubes_list.Find(features["dorsal_tubes"]), GLOB.dorsal_tubes_list.len) + if(features["ethereal_mark"]) + L[DNA_ETHEREAL_MARK_BLOCK] = construct_block(GLOB.ethereal_mark_list.Find(features["ethereal_mark"]), GLOB.ethereal_mark_list.len) + if(features["pod_hair"]) + L[DNA_PLANT_HAIR_BLOCK] = construct_block(GLOB.pod_hair_list.Find(features["pod_hair"]), GLOB.pod_hair_list.len) + if(features["pod_flower"]) + L[DNA_PLANT_FLOWER_BLOCK] = construct_block(GLOB.pod_flower_list.Find(features["pod_flower"]), GLOB.pod_flower_list.len) + + for(var/blocknum in 1 to DNA_FEATURE_BLOCKS) + . += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters) /datum/dna/proc/generate_dna_blocks() var/bonus @@ -163,8 +259,8 @@ if(active) return sequence while(difficulty) - var/randnum = rand(1, length_char(sequence)) - sequence = copytext_char(sequence, 1, randnum) + "X" + copytext_char(sequence, randnum + 1) + var/randnum = rand(1, length(sequence)) + sequence = copytext(sequence, 1, randnum) + "X" + copytext(sequence, randnum + 1) difficulty-- return sequence @@ -177,35 +273,90 @@ . += random_string(DNA_UNIQUE_ENZYMES_LEN, GLOB.hex_characters) return . +///Setter macro used to modify unique identity blocks. +/datum/dna/proc/set_uni_identity_block(blocknum, input) + var/precesing_blocks = copytext(unique_identity, 1, GLOB.total_ui_len_by_block[blocknum]) + var/succeeding_blocks = blocknum < GLOB.total_ui_len_by_block.len ? copytext(unique_identity, GLOB.total_ui_len_by_block[blocknum+1]) : "" + unique_identity = precesing_blocks + input + succeeding_blocks + +///Setter macro used to modify unique features blocks. +/datum/dna/proc/set_uni_feature_block(blocknum, input) + var/precesing_blocks = copytext(unique_features, 1, GLOB.total_uf_len_by_block[blocknum]) + var/succeeding_blocks = blocknum < GLOB.total_uf_len_by_block.len ? copytext(unique_features, GLOB.total_uf_len_by_block[blocknum+1]) : "" + unique_features = precesing_blocks + input + succeeding_blocks + /datum/dna/proc/update_ui_block(blocknumber) - if(!blocknumber || !ishuman(holder)) - return + if(!blocknumber) + CRASH("UI block index is null") + if(!ishuman(holder)) + CRASH("Non-human mobs shouldn't have DNA") var/mob/living/carbon/human/H = holder switch(blocknumber) if(DNA_HAIR_COLOR_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, sanitize_hexcolor(H.hair_color)) + set_uni_identity_block(blocknumber, sanitize_hexcolor(H.hair_color, include_crunch = FALSE)) if(DNA_FACIAL_HAIR_COLOR_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, sanitize_hexcolor(H.facial_hair_color)) + set_uni_identity_block(blocknumber, sanitize_hexcolor(H.facial_hair_color, include_crunch = FALSE)) if(DNA_SKIN_TONE_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len)) + set_uni_identity_block(blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len)) if(DNA_EYE_COLOR_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, sanitize_hexcolor(H.eye_color)) - var/obj/item/organ/eyes/eyes = H.getorgan(/obj/item/organ/eyes) - if(eyes) - eyes.old_eye_color = sanitize_hexcolor(H.eye_color) - eyes.eye_color = sanitize_hexcolor(H.eye_color) + set_uni_identity_block(blocknumber, sanitize_hexcolor(H.eye_color, include_crunch = FALSE)) if(DNA_GENDER_BLOCK) switch(H.gender) if(MALE) - uni_identity = setblock(uni_identity, blocknumber, construct_block(G_MALE, 3)) + set_uni_identity_block(blocknumber, construct_block(G_MALE, 3)) if(FEMALE) - uni_identity = setblock(uni_identity, blocknumber, construct_block(G_FEMALE, 3)) + set_uni_identity_block(blocknumber, construct_block(G_FEMALE, 3)) else - uni_identity = setblock(uni_identity, blocknumber, construct_block(G_PLURAL, 3)) + set_uni_identity_block(blocknumber, construct_block(G_PLURAL, 3)) if(DNA_FACIAL_HAIR_STYLE_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len)) + set_uni_identity_block(blocknumber, construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len)) if(DNA_HAIR_STYLE_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len)) + set_uni_identity_block(blocknumber, construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len)) + +/datum/dna/proc/update_uf_block(blocknumber) + if(!blocknumber) + CRASH("UF block index is null") + if(!ishuman(holder)) + CRASH("Non-human mobs shouldn't have DNA") + switch(blocknumber) + if(DNA_MUTANT_COLOR_BLOCK) + set_uni_feature_block(blocknumber, sanitize_hexcolor(features["mcolor"], include_crunch = FALSE)) + if(DNA_ETHEREAL_COLOR_BLOCK) + set_uni_feature_block(blocknumber, sanitize_hexcolor(features["ethcolor"], include_crunch = FALSE)) + if(DNA_LIZARD_MARKINGS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len)) + if(DNA_LIZARD_TAIL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_lizard.Find(features["tail_lizard"]), GLOB.tails_list_lizard.len)) + if(DNA_SNOUT_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.snouts_list.Find(features["snout"]), GLOB.snouts_list.len)) + if(DNA_HORNS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.horns_list.Find(features["horns"]), GLOB.horns_list.len)) + if(DNA_FRILLS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.frills_list.Find(features["frills"]), GLOB.frills_list.len)) + if(DNA_SPINES_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.spines_list.Find(features["spines"]), GLOB.spines_list.len)) + if(DNA_HUMAN_TAIL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_human.Find(features["tail_human"]), GLOB.tails_list_human.len)) + if(DNA_EARS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.ears_list.Find(features["ears"]), GLOB.ears_list.len)) + if(DNA_MOTH_WINGS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.moth_wings_list.Find(features["moth_wings"]), GLOB.moth_wings_list.len)) + if(DNA_MUSHROOM_CAPS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.caps_list.Find(features["caps"]), GLOB.caps_list.len)) + if(DNA_POLY_TAIL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_polysmorph.Find(features["tail_polysmorph"]), GLOB.tails_list_polysmorph.len)) + if(DNA_POLY_TEETH_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.teeth_list.Find(features["teeth"]), GLOB.teeth_list.len)) + if(DNA_POLY_DOME_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.dome_list.Find(features["dome"]), GLOB.dome_list.len)) + if(DNA_POLY_DORSAL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.dorsal_tubes_list.Find(features["dorsal_tubes"]), GLOB.dorsal_tubes_list.len)) + if(DNA_ETHEREAL_MARK_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.ethereal_mark_list.Find(features["ethereal_mark"]), GLOB.ethereal_mark_list.len)) + if(DNA_PLANT_HAIR_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.pod_hair_list.Find(features["pod_hair"]), GLOB.pod_hair_list.len)) + if(DNA_PLANT_FLOWER_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.pod_flower_list.Find(features["pod_flower"]), GLOB.pod_flower_list.len)) //Please use add_mutation or activate_mutation instead /datum/dna/proc/force_give(datum/mutation/human/HM) @@ -226,7 +377,7 @@ return /datum/dna/proc/is_same_as(datum/dna/D) - if(uni_identity == D.uni_identity && mutation_index == D.mutation_index && real_name == D.real_name) + if(unique_identity == D.unique_identity && mutation_index == D.mutation_index && real_name == D.real_name) if(species.type == D.species.type && features == D.features && blood_type == D.blood_type) return 1 return 0 @@ -259,17 +410,19 @@ //used to update dna UI, UE, and dna.real_name. /datum/dna/proc/update_dna_identity() - uni_identity = generate_uni_identity() + unique_identity = generate_unique_identity() unique_enzymes = generate_unique_enzymes() + unique_features = generate_unique_features() /datum/dna/proc/initialize_dna(newblood_type, skip_index = FALSE) if(newblood_type) blood_type = newblood_type unique_enzymes = generate_unique_enzymes() - uni_identity = generate_uni_identity() + unique_identity = generate_unique_identity() if(!skip_index) //I hate this generate_dna_blocks() features = random_features() + unique_features = generate_unique_features() /datum/dna/stored //subtype used by brain mob's stored_dna @@ -342,6 +495,7 @@ //Do not use force_transfer_mutations for stuff like cloners without some precautions, otherwise some conditional mutations could break (timers, drill hat etc) if(newfeatures) dna.features = newfeatures + dna.generate_unique_features() if(mrace) var/datum/species/newrace = new mrace.type @@ -356,7 +510,7 @@ dna.blood_type = newblood_type if(ui) - dna.uni_identity = ui + dna.unique_identity = ui updateappearance(icon_update=0) if(LAZYLEN(mutation_index)) @@ -390,7 +544,7 @@ if(!has_dna()) return - switch(deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 3)) + switch(deconstruct_block(get_uni_identity_block(dna.unique_identity, DNA_GENDER_BLOCK), 3)) if(G_MALE) gender = MALE if(G_FEMALE) @@ -400,15 +554,58 @@ /mob/living/carbon/human/updateappearance(icon_update=1, mutcolor_update=0, mutations_overlay_update=0) ..() - var/structure = dna.uni_identity - hair_color = sanitize_hexcolor(getblock(structure, DNA_HAIR_COLOR_BLOCK)) - facial_hair_color = sanitize_hexcolor(getblock(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)) - skin_tone = GLOB.skin_tones[deconstruct_block(getblock(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)] - eye_color = sanitize_hexcolor(getblock(structure, DNA_EYE_COLOR_BLOCK)) - facial_hair_style = GLOB.facial_hair_styles_list[deconstruct_block(getblock(structure, DNA_FACIAL_HAIR_STYLE_BLOCK), GLOB.facial_hair_styles_list.len)] - hair_style = GLOB.hair_styles_list[deconstruct_block(getblock(structure, DNA_HAIR_STYLE_BLOCK), GLOB.hair_styles_list.len)] + var/structure = dna.unique_identity + hair_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_HAIR_COLOR_BLOCK)) + facial_hair_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)) + skin_tone = GLOB.skin_tones[deconstruct_block(get_uni_identity_block(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)] + eye_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_BLOCK)) + facial_hair_style = GLOB.facial_hair_styles_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIR_STYLE_BLOCK), GLOB.facial_hair_styles_list.len)] + hair_style = GLOB.hair_styles_list[deconstruct_block(get_uni_identity_block(structure, DNA_HAIR_STYLE_BLOCK), GLOB.hair_styles_list.len)] + var/features = dna.unique_features + if(dna.features["mcolor"]) + dna.features["mcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_MUTANT_COLOR_BLOCK)) + if(dna.features["ethcolor"]) + dna.features["ethcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_ETHEREAL_COLOR_BLOCK)) + if(dna.features["body_markings"]) + dna.features["body_markings"] = GLOB.body_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_MARKINGS_BLOCK), GLOB.body_markings_list.len)] + if(dna.features["tail_lizard"]) + dna.features["tail_lizard"] = GLOB.tails_list_lizard[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_TAIL_BLOCK), GLOB.tails_list_lizard.len)] + if(dna.features["snout"]) + dna.features["snout"] = GLOB.snouts_list[deconstruct_block(get_uni_feature_block(features, DNA_SNOUT_BLOCK), GLOB.snouts_list.len)] + if(dna.features["horns"]) + dna.features["horns"] = GLOB.horns_list[deconstruct_block(get_uni_feature_block(features, DNA_HORNS_BLOCK), GLOB.horns_list.len)] + if(dna.features["frills"]) + dna.features["frills"] = GLOB.frills_list[deconstruct_block(get_uni_feature_block(features, DNA_FRILLS_BLOCK), GLOB.frills_list.len)] + if(dna.features["spines"]) + dna.features["spines"] = GLOB.spines_list[deconstruct_block(get_uni_feature_block(features, DNA_SPINES_BLOCK), GLOB.spines_list.len)] + if(dna.features["tail_human"]) + dna.features["tail_human"] = GLOB.tails_list_human[deconstruct_block(get_uni_feature_block(features, DNA_HUMAN_TAIL_BLOCK), GLOB.tails_list_human.len)] + if(dna.features["ears"]) + dna.features["ears"] = GLOB.ears_list[deconstruct_block(get_uni_feature_block(features, DNA_EARS_BLOCK), GLOB.ears_list.len)] + if(dna.features["moth_wings"]) + var/genetic_value = GLOB.moth_wings_list[deconstruct_block(get_uni_feature_block(features, DNA_MOTH_WINGS_BLOCK), GLOB.moth_wings_list.len)] + dna.features["original_moth_wings"] = genetic_value + if(dna.features["moth_wings"] != "Burnt Off") + dna.features["moth_wings"] = genetic_value + if(dna.features["caps"]) + dna.features["caps"] = GLOB.caps_list[deconstruct_block(get_uni_feature_block(features, DNA_MUSHROOM_CAPS_BLOCK), GLOB.caps_list.len)] + // if(features["tail_polysmorph"]) + // dna.features["tail_polysmorph"] = GLOB.tails_list_polysmorph[deconstruct_block(get_uni_feature_block(features, DNA_POLY_TAIL_BLOCK), GLOB.tails_list_polysmorph.len)] + if(dna.features["teeth"]) + dna.features["teeth"] = GLOB.teeth_list[deconstruct_block(get_uni_feature_block(features, DNA_POLY_TEETH_BLOCK), GLOB.teeth_list.len)] + if(dna.features["dome"]) + dna.features["dome"] = GLOB.dome_list[deconstruct_block(get_uni_feature_block(features, DNA_POLY_DOME_BLOCK), GLOB.dome_list.len)] + if(dna.features["dorsal_tubes"]) + dna.features["dorsal_tubes"] = GLOB.dorsal_tubes_list[deconstruct_block(get_uni_feature_block(features, DNA_POLY_DORSAL_BLOCK), GLOB.dorsal_tubes_list.len)] + if(dna.features["ethereal_mark"]) + dna.features["ethereal_mark"] = GLOB.ethereal_mark_list[deconstruct_block(get_uni_feature_block(features, DNA_ETHEREAL_MARK_BLOCK), GLOB.ethereal_mark_list.len)] + if(dna.features["pod_hair"]) + dna.features["pod_hair"] = GLOB.pod_hair_list[deconstruct_block(get_uni_feature_block(features, DNA_PLANT_HAIR_BLOCK), GLOB.pod_hair_list.len)] + if(dna.features["pod_flower"]) + dna.features["pod_flower"] = GLOB.pod_flower_list[deconstruct_block(get_uni_feature_block(features, DNA_PLANT_FLOWER_BLOCK), GLOB.pod_flower_list.len)] + if(icon_update) - update_body() + dna.species.handle_body(src) // We want 'update_body_parts()' to be called only if mutcolor_update is TRUE, so no 'update_body()' here. update_hair() if(mutcolor_update) update_body_parts() @@ -476,22 +673,6 @@ /////////////////////////// DNA HELPER-PROCS ////////////////////////////// -/proc/getleftblocks(input,blocknumber,blocksize) - if(blocknumber > 1) - return copytext_char(input,1,((blocksize*blocknumber)-(blocksize-1))) - -/proc/getrightblocks(input,blocknumber,blocksize) - if(blocknumber < (length(input)/blocksize)) - return copytext_char(input,blocksize*blocknumber+1,length(input)+1) - -/proc/getblock(input, blocknumber, blocksize=DNA_BLOCK_SIZE) - return copytext_char(input, blocksize*(blocknumber-1)+1, (blocksize*blocknumber)+1) - -/proc/setblock(istring, blocknumber, replacement, blocksize=DNA_BLOCK_SIZE) - if(!istring || !blocknumber || !replacement || !blocksize) - return 0 - return getleftblocks(istring, blocknumber, blocksize) + replacement + getrightblocks(istring, blocknumber, blocksize) - /datum/dna/proc/mutation_in_sequence(mutation) if(!mutation) return @@ -503,15 +684,15 @@ return TRUE -/mob/living/carbon/proc/randmut(list/candidates, difficulty = 2) +/mob/living/carbon/proc/random_mutate(list/candidates, difficulty = 2) if(!has_dna()) - return + CRASH("[src] does not have DNA") var/mutation = pick(candidates) . = dna.add_mutation(mutation) -/mob/living/carbon/proc/easy_randmut(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE) +/mob/living/carbon/proc/easy_random_mutate(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE) if(!has_dna()) - return + CRASH("[src] does not have DNA") var/list/mutations = list() if(quality & POSITIVE) mutations += GLOB.good_mutations @@ -534,37 +715,47 @@ HM.scrambled = TRUE return TRUE -/mob/living/carbon/proc/randmuti() +/mob/living/carbon/proc/random_mutate_unique_identity() if(!has_dna()) - return + CRASH("[src] does not have DNA") var/num = rand(1, DNA_UNI_IDENTITY_BLOCKS) - var/newdna = setblock(dna.uni_identity, num, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) - dna.uni_identity = newdna + dna.set_uni_feature_block(num, random_string(GET_UI_BLOCK_LEN(num), GLOB.hex_characters)) updateappearance(mutations_overlay_update=1) +/mob/living/carbon/proc/random_mutate_unique_features() + if(!has_dna()) + CRASH("[src] does not have DNA") + var/num = rand(1, DNA_FEATURE_BLOCKS) + dna.set_uni_feature_block(num, random_string(GET_UF_BLOCK_LEN(num), GLOB.hex_characters)) + updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE) + /mob/living/carbon/proc/clean_dna() if(!has_dna()) - return + CRASH("[src] does not have DNA") dna.remove_all_mutations() -/mob/living/carbon/proc/clean_randmut(list/candidates, difficulty = 2) +/mob/living/carbon/proc/clean_random_mutate(list/candidates, difficulty = 2) clean_dna() - randmut(candidates, difficulty) + random_mutate(candidates, difficulty) -/proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, probability) +/proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, uf=FALSE, probability) if(!M.has_dna()) - return 0 + CRASH("[M] does not have DNA") if(se) for(var/i=1, i<=DNA_MUTATION_BLOCKS, i++) if(prob(probability)) M.dna.generate_dna_blocks() M.domutcheck() if(ui) - for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++) + for(var/blocknum in 1 to DNA_UNI_IDENTITY_BLOCKS) if(prob(probability)) - M.dna.uni_identity = setblock(M.dna.uni_identity, i, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) - M.updateappearance(mutations_overlay_update=1) - return 1 + M.dna.set_uni_feature_block(blocknum, random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters)) + if(uf) + for(var/blocknum in 1 to DNA_FEATURE_BLOCKS) + if(prob(probability)) + M.dna.set_uni_feature_block(blocknum, random_string(GET_UF_BLOCK_LEN(blocknum), GLOB.hex_characters)) + if(ui || uf) + M.updateappearance(mutcolor_update=uf, mutations_overlay_update=1) //value in range 1 to values. values must be greater than 0 //all arguments assumed to be positive integers @@ -583,6 +774,12 @@ value = values return value +/proc/get_uni_identity_block(identity, blocknum) + return copytext(identity, GLOB.total_ui_len_by_block[blocknum], LAZYACCESS(GLOB.total_ui_len_by_block, blocknum+1)) + +/proc/get_uni_feature_block(features, blocknum) + return copytext(features, GLOB.total_uf_len_by_block[blocknum], LAZYACCESS(GLOB.total_uf_len_by_block, blocknum+1)) + /////////////////////////// DNA HELPER-PROCS /mob/living/carbon/human/proc/something_horrible(ignore_stability) diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index 4c0a1636a83a..df537367b3e7 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -54,7 +54,7 @@ possible = list() var/list/prints = sniffed.return_fingerprints() for(var/mob/living/carbon/C in GLOB.carbon_list) - if(prints[md5(C.dna.uni_identity)]) + if(prints[md5(C.dna.unique_identity)]) possible |= C if(!length(possible)) to_chat(user,span_warning("Despite your best efforts, there are no scents to be found on [sniffed]...")) diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index f8a4895721b8..915b93668527 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -36,12 +36,15 @@ to_chat(owner, text_gain_indication) var/mob/new_mob if(prob(95)) - if(prob(50)) - new_mob = owner.easy_randmut(NEGATIVE + MINOR_NEGATIVE) - else - new_mob = owner.randmuti() + switch(rand(1,3)) + if(1) + new_mob = owner.easy_random_mutate(NEGATIVE + MINOR_NEGATIVE) + if(2) + new_mob = owner.random_mutate_unique_identity() + if(3) + new_mob = owner.random_mutate_unique_features() else - new_mob = owner.easy_randmut(POSITIVE) + new_mob = owner.easy_random_mutate(POSITIVE) if(new_mob && ismob(new_mob)) owner = new_mob . = owner diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 7e6284b5409c..90de74143284 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -36,12 +36,13 @@ var/mob/living/carbon/human/H = L if(H.dna && !HAS_TRAIT(H, TRAIT_GENELESS)) if(prob(max(0,100-resist))) - H.randmuti() + H.random_mutate_unique_identity() + H.random_mutate_unique_features() if(prob(50)) if(prob(90)) - H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) + H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE) else - H.easy_randmut(POSITIVE) + H.easy_random_mutate(POSITIVE) H.domutcheck() L.rad_act(20) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 90fc5b0afe02..16dc6488d6bd 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -231,9 +231,9 @@ GLOBAL_VAR_INIT(clones, 0) var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) H.dna.remove_mutation_group(unclean_mutations) if(efficiency > 5 && prob(20)) - H.easy_randmut(POSITIVE) + H.easy_random_mutate(POSITIVE) if(efficiency < 3 && prob(50)) - var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) + var/mob/M = H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE) if(ismob(M)) H = M if((AGENDER || MGENDER || FGENDER) in H.dna.species.species_traits) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index a7ad64fb772e..d0d273f272dd 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -580,7 +580,7 @@ R.fields["name"] = mob_occupant.real_name R.fields["id"] = copytext_char(md5(mob_occupant.real_name), 2, 6) R.fields["UE"] = dna.unique_enzymes - R.fields["UI"] = dna.uni_identity + R.fields["UI"] = dna.unique_identity R.fields["SE"] = dna.mutation_index R.fields["blood_type"] = dna.blood_type R.fields["features"] = dna.features @@ -616,7 +616,7 @@ var/datum/data/record/old_record = find_record("mindref", REF(mob_occupant.mind), records) if(body_only) old_record = find_record("UE", dna.unique_enzymes, records) //Body-only records cannot be identified by mind, so we use the DNA - if(old_record && ((old_record.fields["UI"] != dna.uni_identity) || (!old_record.fields["body_only"]))) //Never overwrite a mind-and-body record if it exists + if(old_record && ((old_record.fields["UI"] != dna.unique_identity) || (!old_record.fields["body_only"]))) //Never overwrite a mind-and-body record if it exists old_record = null if(old_record) records -= old_record diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index 2da37ff5174c..c613190d8fc7 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -34,6 +34,11 @@ /// Flag for the mutation ref search system. Search will include advanced injector mutations #define SEARCH_ADV_INJ 8 +#define ENZYME_COPY_BASE_COOLDOWN (60 SECONDS) + +#define RAD_PULSE_UNIQUE_IDENTITY "ui" +#define RAD_PULSE_UNIQUE_FEATURES "uf" + /obj/machinery/computer/scan_consolenew name = "DNA Console" desc = "Scan DNA." @@ -89,7 +94,11 @@ var/rad_pulse_index = 0 /// World time when the enzyme pulse should complete var/rad_pulse_timer = 0 - + /// Which dna string to edit with the pulse + var/rad_pulse_type + /// Cooldown for the genetic makeup transfer actions. + COOLDOWN_DECLARE(enzyme_copy_timer) + /// Used for setting tgui data - Whether the connected DNA Scanner is usable var/can_use_scanner = FALSE /// Used for setting tgui data - Whether the current DNA Scanner occupant is viable for genetic modification @@ -138,7 +147,7 @@ // This is for pulsing the UI element with radiation as part of genetic makeup // If rad_pulse_index > 0 then it means we're attempting a rad pulse - if((rad_pulse_index > 0) && (rad_pulse_timer <= world.time)) + if(((rad_pulse_index > 0) && (rad_pulse_timer <= world.time) && (rad_pulse_type == RAD_PULSE_UNIQUE_IDENTITY || rad_pulse_type == RAD_PULSE_UNIQUE_FEATURES))) rad_pulse() return @@ -289,7 +298,8 @@ data["subjectRads"] = scanner_occupant.radiation/(RAD_MOB_SAFE/100) data["subjectEnzymes"] = scanner_occupant.dna.unique_enzymes data["isMonkey"] = ismonkey(scanner_occupant) - data["subjectUNI"] = scanner_occupant.dna.uni_identity + data["subjectUNI"] = scanner_occupant.dna.unique_identity + data["subjectUF"] = scanner_occupant.dna.unique_features data["storage"]["occupant"] = tgui_occupant_mutations //data["subjectMutations"] = tgui_occupant_mutations else @@ -489,12 +499,12 @@ // X to allow highlighting logic to work on the tgui interface. if(newgene == "X") var/defaultseq = scanner_occupant.dna.default_mutation_genes[path] - defaultseq = copytext_char(defaultseq, 1, genepos) + newgene + copytext_char(defaultseq, genepos + 1) + defaultseq = copytext(defaultseq, 1, genepos) + newgene + copytext(defaultseq, genepos + 1) scanner_occupant.dna.default_mutation_genes[path] = defaultseq // Copy genome to scanner occupant and do some basic mutation checks as // we've increased the occupant rads - sequence = copytext_char(sequence, 1, genepos) + newgene + copytext_char(sequence, genepos + 1) + sequence = copytext(sequence, 1, genepos) + newgene + copytext(sequence, genepos + 1) scanner_occupant.dna.mutation_index[path] = sequence scanner_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected_scanner.damage_coeff scanner_occupant.domutcheck() @@ -1002,8 +1012,9 @@ // Set the new information genetic_makeup_buffer[buffer_index] = list( "label"="Slot [buffer_index]:[scanner_occupant.real_name]", - "UI"=scanner_occupant.dna.uni_identity, + "UI"=scanner_occupant.dna.unique_identity, "UE"=scanner_occupant.dna.unique_enzymes, + "UF"=scanner_occupant.dna.unique_features, "name"=scanner_occupant.real_name, "blood_type"=scanner_occupant.dna.blood_type) @@ -1050,6 +1061,7 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // "mixed" - Combination of both ue and ui if("makeup_injector") // Convert the index to a number and clamp within the array range, then @@ -1093,6 +1105,21 @@ I = new /obj/item/dnainjector/timed(loc) I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) + // If there is a connected scanner, we can use its upgrades to reduce + // the radiation generated by this injector + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + if("uf") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["name"] || !buffer_slot["UF"] || !buffer_slot["blood_type"]) + to_chat(usr,"Genetic data corrupted, unable to create injector.") + return + + I = new /obj/item/dnainjector/timed(loc) + I.fields = list("name"=buffer_slot["name"], "UF"=buffer_slot["UF"]) + // If there is a connected scanner, we can use its upgrades to reduce // the radiation generated by this injector if(scanner_operational()) @@ -1101,12 +1128,12 @@ // GUARD CHECK - There's currently no way to save partial genetic data. // However, if this is the case, we can't make a complete injector and // this catches that edge case - if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["UF"] || !buffer_slot["blood_type"]) to_chat(usr,span_warning("Genetic data corrupted, unable to create injector.")) return I = new /obj/item/dnainjector/timed(loc) - I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) + I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "UF"=buffer_slot["UF"], "blood_type"=buffer_slot["blood_type"]) // If there is a connected scanner, we can use its upgrades to reduce // the radiation generated by this injector @@ -1129,6 +1156,7 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // "mixed" - Combination of both ue and ui if("makeup_apply") // GUARD CHECK - Can we genetically modify the occupant? Includes scanner @@ -1166,6 +1194,7 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // "mixed" - Combination of both ue and ui if("makeup_delay") // Convert the index to a number and clamp within the array range, then @@ -1189,6 +1218,10 @@ // Attempts to modify the indexed element of the Unique Identity string // This is a time delayed action that is handled in process() // ---------------------------------------------------------------------- // + // params["type"] - Type of genetic makeup string to edit + // Expected results: + // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // params["index"] - The BYOND index of the Unique Identity string to // attempt to modify if("makeup_pulse") @@ -1197,9 +1230,16 @@ if(!can_modify_occupant()) return - // Set the appropriate timer and index to pulse. This is then managed + // Set the appropriate timer, string, and index to pulse. This is then managed // later on in process() - var/len = length_char(scanner_occupant.dna.uni_identity) + var/type = params["type"] + rad_pulse_type = type + var/len + switch(type) + if("ui") + len = length(scanner_occupant.dna.unique_identity) + if("uf") + len = length(scanner_occupant.dna.unique_features) rad_pulse_timer = world.time + (radduration*10) rad_pulse_index = WRAP(text2num(params["index"]), 1, len+1) begin_processing() @@ -1424,11 +1464,24 @@ if(!buffer_slot["UI"]) to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data.")) return FALSE - scanner_occupant.dna.uni_identity = buffer_slot["UI"] + scanner_occupant.dna.unique_identity = buffer_slot["UI"] scanner_occupant.updateappearance(mutations_overlay_update=1) scanner_occupant.radiation += rad_increase scanner_occupant.domutcheck() return TRUE + if("uf") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["UF"]) + to_chat(usr,"Genetic data corrupted, unable to apply genetic data.") + return FALSE + COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) + scanner_occupant.dna.unique_features = buffer_slot["UF"] + scanner_occupant.updateappearance(mutcolor_update=1, mutations_overlay_update=1) + scanner_occupant.radiation += rad_increase + scanner_occupant.domutcheck() + return TRUE if("ue") // GUARD CHECK - There's currently no way to save partial genetic data. // However, if this is the case, we can't make a complete injector and @@ -1436,6 +1489,7 @@ if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data.")) return FALSE + COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) scanner_occupant.real_name = buffer_slot["name"] scanner_occupant.name = buffer_slot["name"] scanner_occupant.dna.unique_enzymes = buffer_slot["UE"] @@ -1447,11 +1501,13 @@ // GUARD CHECK - There's currently no way to save partial genetic data. // However, if this is the case, we can't make a complete injector and // this catches that edge case - if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["UF"] || !buffer_slot["blood_type"]) to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data.")) return FALSE - scanner_occupant.dna.uni_identity = buffer_slot["UI"] - scanner_occupant.updateappearance(mutations_overlay_update=1) + COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) + scanner_occupant.dna.unique_identity = buffer_slot["UI"] + scanner_occupant.dna.unique_features = buffer_slot["UF"] + scanner_occupant.updateappearance(mutcolor_update=1, mutations_overlay_update=1) scanner_occupant.real_name = buffer_slot["name"] scanner_occupant.name = buffer_slot["name"] scanner_occupant.dna.unique_enzymes = buffer_slot["UE"] @@ -1943,20 +1999,36 @@ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner // operational guard checks. // If we can't, abort the procedure. - if(!can_modify_occupant()) + if(!can_modify_occupant()|| (rad_pulse_type != RAD_PULSE_UNIQUE_IDENTITY && rad_pulse_type != RAD_PULSE_UNIQUE_FEATURES)) rad_pulse_index = 0 end_processing() return - var/len = length_char(scanner_occupant.dna.uni_identity) + var/len + switch(rad_pulse_type) + if(RAD_PULSE_UNIQUE_IDENTITY) + len = length(scanner_occupant.dna.unique_identity) + if(RAD_PULSE_UNIQUE_FEATURES) + len = length(scanner_occupant.dna.unique_features) + var/num = randomize_radiation_accuracy(rad_pulse_index, radduration + (connected_scanner.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2 //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low - var/hex = copytext_char(scanner_occupant.dna.uni_identity, num, num+1) + var/hex + switch(rad_pulse_type) + if(RAD_PULSE_UNIQUE_IDENTITY) + hex = copytext(scanner_occupant.dna.unique_identity, num, num+1) + if(RAD_PULSE_UNIQUE_FEATURES) + hex = copytext(scanner_occupant.dna.unique_features, num, num+1) hex = scramble(hex, radstrength, radduration) - scanner_occupant.dna.uni_identity = copytext_char(scanner_occupant.dna.uni_identity, 1, num) + hex + copytext_char(scanner_occupant.dna.uni_identity, num + 1) - scanner_occupant.updateappearance(mutations_overlay_update=1) + switch(rad_pulse_type) + if(RAD_PULSE_UNIQUE_IDENTITY) + scanner_occupant.dna.unique_identity = copytext(scanner_occupant.dna.unique_identity, 1, num) + hex + copytext(scanner_occupant.dna.unique_identity, num + 1) + if(RAD_PULSE_UNIQUE_FEATURES) + scanner_occupant.dna.unique_features = copytext(scanner_occupant.dna.unique_features, 1, num) + hex + copytext(scanner_occupant.dna.unique_features, num + 1) + scanner_occupant.updateappearance(mutcolor_update=1, mutations_overlay_update=1) rad_pulse_index = 0 + rad_pulse_type = null end_processing() return diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm index 416c43047ac2..4a83fcffc850 100644 --- a/code/game/machinery/exp_cloner.dm +++ b/code/game/machinery/exp_cloner.dm @@ -26,9 +26,9 @@ var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) H.dna.remove_mutation_group(unclean_mutations) if(efficiency > 5 && prob(20)) - H.easy_randmut(POSITIVE) + H.easy_random_mutate(POSITIVE) if(efficiency < 3 && prob(50)) - var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) + var/mob/M = H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE) if(ismob(M)) H = M @@ -298,6 +298,6 @@ temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else - pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, clone_species, dna.features, mob_occupant.faction) + pod.growclone(mob_occupant.real_name, dna.unique_identity, dna.mutation_index, null, null, clone_species, dna.features, mob_occupant.faction) temp = "[mob_occupant.real_name] => Cloning data sent to pod." playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm index 5abcc9e2a30d..5e0ffa557ca4 100644 --- a/code/game/objects/items/cosmetics.dm +++ b/code/game/objects/items/cosmetics.dm @@ -259,7 +259,7 @@ if(!new_grad_style) return - var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference","#"+human_target.grad_color) as color|null + var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference",human_target.grad_color) as color|null if(!new_grad_color) return diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index d1f7fee482c6..662139521ebb 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -40,8 +40,11 @@ M.name = M.real_name M.dna.blood_type = fields["blood_type"] if(fields["UI"]) //UI+UE - M.dna.uni_identity = merge_text(M.dna.uni_identity, fields["UI"]) - M.updateappearance(mutations_overlay_update=1) + M.dna.unique_identity = merge_text(M.dna.unique_identity, fields["UI"]) + if(fields["UF"]) + M.dna.unique_features = merge_text(M.dna.unique_features, fields["UF"]) + if(fields["UI"] || fields["UF"]) + M.updateappearance(mutcolor_update=1, mutations_overlay_update=1) log_attack("[log_msg] [loc_name(user)]") return TRUE return FALSE @@ -503,10 +506,16 @@ M.dna.temporary_mutations[UE_CHANGED] = endtime if(fields["UI"]) //UI+UE if(!M.dna.previous["UI"]) - M.dna.previous["UI"] = M.dna.uni_identity - M.dna.uni_identity = merge_text(M.dna.uni_identity, fields["UI"]) - M.updateappearance(mutations_overlay_update=1) + M.dna.previous["UI"] = M.dna.unique_identity + M.dna.unique_identity = merge_text(M.dna.unique_identity, fields["UI"]) M.dna.temporary_mutations[UI_CHANGED] = endtime + if(fields["UF"]) //UI+UE + if(!M.dna.previous["UF"]) + M.dna.previous["UF"] = M.dna.unique_features + M.dna.unique_features = merge_text(M.dna.unique_features, fields["UF"]) + M.dna.temporary_mutations[UF_CHANGED] = endtime + if(fields["UI"] || fields["UF"]) + M.updateappearance(mutcolor_update=1, mutations_overlay_update=1) log_attack("[log_msg] [loc_name(user)]") return TRUE else diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index f7143171c420..f9c06f640130 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -173,15 +173,16 @@ H.dna.update_ui_block(DNA_SKIN_TONE_BLOCK) if((MUTCOLORS in H.dna.species.species_traits) && !(NOCOLORCHANGE in H.dna.species.species_traits)) - var/new_mutantcolor = input(user, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null - if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - - if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright - H.dna.features["mcolor"] = sanitize_hexcolor(new_mutantcolor) + var/new_mutantcolor = input(user, "Choose your skin color:", "Race change", H.dna.features["mcolor"]) as color|null + if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) - else + if(ReadHSV(temp_hsv)[3] >= ReadHSV("#777777")[3]) // mutantcolors must be bright + H.dna.features["mcolor"] = sanitize_hexcolor(new_mutantcolor) + H.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK) + else to_chat(H, span_notice("Invalid color. Your color is not bright enough.")) H.update_body() @@ -220,21 +221,21 @@ if(hairchoice == "Style") //So you just want to use a mirror then? ..() else - var/new_hair_color = input(H, "Choose your hair color", "Hair Color","#"+H.hair_color) as color|null + var/new_hair_color = input(H, "Choose your hair color", "Hair Color",H.hair_color) as color|null if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) return if(new_hair_color) H.hair_color = sanitize_hexcolor(new_hair_color) H.dna.update_ui_block(DNA_HAIR_COLOR_BLOCK) if(H.gender == "male") - var/new_face_color = input(H, "Choose your facial hair color", "Hair Color","#"+H.facial_hair_color) as color|null + var/new_face_color = input(H, "Choose your facial hair color", "Hair Color",H.facial_hair_color) as color|null if(new_face_color) H.facial_hair_color = sanitize_hexcolor(new_face_color) H.dna.update_ui_block(DNA_FACIAL_HAIR_COLOR_BLOCK) H.update_hair() if(BODY_ZONE_PRECISE_EYES) - var/new_eye_color = input(H, "Choose your eye color", "Eye Color","#"+H.eye_color) as color|null + var/new_eye_color = input(H, "Choose your eye color", "Eye Color",H.eye_color) as color|null if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) return if(new_eye_color) diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index e497cb44f278..c3bb09e0b7f5 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -18,13 +18,13 @@ H.skin_tone = random_skin_tone() H.hair_style = random_hair_style(H.gender) H.facial_hair_style = random_facial_hair_style(H.gender) - H.hair_color = random_short_color() + H.hair_color = "#[random_color()]" H.facial_hair_color = H.hair_color H.eye_color = random_eye_color() H.dna.blood_type = random_blood_type() // Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant. - H.dna.features["mcolor"] = random_short_color() + H.dna.features["mcolor"] = "#[random_color()]" H.dna.features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)] H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard) H.dna.features["tail_polysmorph"] = pick(GLOB.tails_list_polysmorph) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index b1d5c03de041..81e6bf5a7abe 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -289,7 +289,7 @@ dat += "
| Name | Fingerprints |
|---|---|
| [H] | [md5(H.dna.uni_identity)] |
| [H] | [md5(H.dna.unique_identity)] |