diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 34bbaf45444..18df3583fae 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -64,6 +64,7 @@ #define UE_CHANGED "ue changed" #define UI_CHANGED "ui changed" +#define UF_CHANGED "uf changed" #define CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY 204 @@ -92,6 +93,23 @@ #define DNA_SKIN_TONE_BLOCK 3 #define DNA_UNI_IDENTITY_BLOCKS 7 +#define DNA_FEATURE_BLOCKS 15 +#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_MOTH_ANTENNAE_BLOCK 12 +#define DNA_MOTH_MARKINGS_BLOCK 13 +#define DNA_MUSHROOM_CAPS_BLOCK 14 +#define DNA_MONKEY_TAIL_BLOCK 15 + #define DNA_SEQUENCE_LENGTH 4 #define DNA_MUTATION_BLOCKS 8 #define DNA_UNIQUE_ENZYMES_LEN 32 diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm index a45c0f4bb72..6945d7e9f1a 100644 --- a/code/datums/components/forensics.dm +++ b/code/datums/components/forensics.dm @@ -91,7 +91,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 e8717180d5f..f0f26775af4 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -260,7 +260,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 @@ -315,7 +315,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/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm index b6395920f4c..094d12eb051 100644 --- a/code/datums/diseases/advance/symptoms/genetics.dm +++ b/code/datums/diseases/advance/symptoms/genetics.dm @@ -61,7 +61,7 @@ Bonus switch(A.stage) if(4, 5) to_chat(C, span_warning("[pick("Your skin feels itchy.", "You feel light headed.")]")) - C.easy_randmut((NEGATIVE | MINOR_NEGATIVE | POSITIVE) - excludemuts, TRUE, TRUE, TRUE, mutadone_proof) + C.easy_random_mutate((NEGATIVE | MINOR_NEGATIVE | POSITIVE) - excludemuts, TRUE, TRUE, TRUE, mutadone_proof) /datum/symptom/genetic_mutation/End(datum/disease/advance/A) . = ..() diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm index 954ca675ab5..7cbcbb97394 100644 --- a/code/datums/diseases/retrovirus.dm +++ b/code/datums/diseases/retrovirus.dm @@ -61,20 +61,26 @@ if(DT_PROB(5, delta_time)) to_chat(affected_mob, span_danger("Your entire body vibrates.")) if(DT_PROB(19, delta_time)) - 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(restcure && affected_mob.body_position == LYING_DOWN && DT_PROB(10, delta_time)) to_chat(affected_mob, span_notice("You feel better.")) cure() return FALSE if(4) if(DT_PROB(37, delta_time)) - if(prob(50)) - scramble_dna(affected_mob, 1, 0, rand(50,75)) - else - scramble_dna(affected_mob, 0, 1, rand(50,75)) + 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)) if(restcure && affected_mob.body_position == LYING_DOWN && DT_PROB(2.5, delta_time)) to_chat(affected_mob, span_notice("You feel better.")) cure() diff --git a/code/datums/dna.dm b/code/datums/dna.dm index a8714fa8c85..6d65dbe4690 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -1,11 +1,15 @@ /////////////////////////// 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 @@ -39,9 +43,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() @@ -53,7 +58,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 @@ -89,7 +95,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) @@ -120,6 +126,47 @@ . += random_string(DNA_BLOCK_SIZE,GLOB.hex_characters) return . +/datum/dna/proc/generate_unique_features() + var/list/data = list() + + var/list/L = new /list(DNA_FEATURE_BLOCKS) + + if(features["mcolor"]) + L[DNA_MUTANT_COLOR_BLOCK] = sanitize_hexcolor(features["mcolor"]) + if(features["ethcolor"]) + L[DNA_ETHEREAL_COLOR_BLOCK] = sanitize_hexcolor(features["ethcolor"]) + 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["moth_antennae"] != "Burnt Off") + L[DNA_MOTH_ANTENNAE_BLOCK] = construct_block(GLOB.moth_antennae_list.Find(features["moth_antennae"]), GLOB.moth_antennae_list.len) + if(features["moth_markings"]) + L[DNA_MOTH_MARKINGS_BLOCK] = construct_block(GLOB.moth_markings_list.Find(features["moth_markings"]), GLOB.moth_markings_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_monkey"]) + L[DNA_MONKEY_TAIL_BLOCK] = construct_block(GLOB.tails_list_monkey.Find(features["tail_monkey"]), GLOB.tails_list_monkey.len) + + for(var/i in 1 to DNA_FEATURE_BLOCKS) + data += (L[i] || random_string(DNA_BLOCK_SIZE,GLOB.hex_characters)) + + return data.Join() + /datum/dna/proc/generate_dna_blocks() var/bonus if(species?.inert_mutation) @@ -158,8 +205,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 @@ -173,30 +220,69 @@ return . /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) - setblock(uni_identity, blocknumber, sanitize_hexcolor(H.hair_color)) + setblock(unique_identity, blocknumber, sanitize_hexcolor(H.hair_color)) if(DNA_FACIAL_HAIR_COLOR_BLOCK) - setblock(uni_identity, blocknumber, sanitize_hexcolor(H.facial_hair_color)) + setblock(unique_identity, blocknumber, sanitize_hexcolor(H.facial_hair_color)) if(DNA_SKIN_TONE_BLOCK) - setblock(uni_identity, blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len)) + setblock(unique_identity, blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len)) if(DNA_EYE_COLOR_BLOCK) - setblock(uni_identity, blocknumber, sanitize_hexcolor(H.eye_color)) + setblock(unique_identity, blocknumber, sanitize_hexcolor(H.eye_color)) if(DNA_GENDER_BLOCK) switch(H.gender) if(MALE) - setblock(uni_identity, blocknumber, construct_block(G_MALE, 3)) + setblock(unique_identity, blocknumber, construct_block(G_MALE, 3)) if(FEMALE) - setblock(uni_identity, blocknumber, construct_block(G_FEMALE, 3)) + setblock(unique_identity, blocknumber, construct_block(G_FEMALE, 3)) else - setblock(uni_identity, blocknumber, construct_block(G_PLURAL, 3)) + setblock(unique_identity, blocknumber, construct_block(G_PLURAL, 3)) if(DNA_FACIAL_HAIRSTYLE_BLOCK) - setblock(uni_identity, blocknumber, construct_block(GLOB.facial_hairstyles_list.Find(H.facial_hairstyle), GLOB.facial_hairstyles_list.len)) + setblock(unique_identity, blocknumber, construct_block(GLOB.facial_hairstyles_list.Find(H.facial_hairstyle), GLOB.facial_hairstyles_list.len)) if(DNA_HAIRSTYLE_BLOCK) - setblock(uni_identity, blocknumber, construct_block(GLOB.hairstyles_list.Find(H.hairstyle), GLOB.hairstyles_list.len)) + setblock(unique_identity, blocknumber, construct_block(GLOB.hairstyles_list.Find(H.hairstyle), GLOB.hairstyles_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) + setblock(unique_features, blocknumber, sanitize_hexcolor(features["mcolor"])) + if(DNA_ETHEREAL_COLOR_BLOCK) + setblock(unique_features, blocknumber, sanitize_hexcolor(features["ethcolor"])) + if(DNA_LIZARD_MARKINGS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len)) + if(DNA_LIZARD_TAIL_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.tails_list_lizard.Find(features["tail_lizard"]), GLOB.tails_list_lizard.len)) + if(DNA_SNOUT_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.snouts_list.Find(features["snout"]), GLOB.snouts_list.len)) + if(DNA_HORNS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.horns_list.Find(features["horns"]), GLOB.horns_list.len)) + if(DNA_FRILLS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.frills_list.Find(features["frills"]), GLOB.frills_list.len)) + if(DNA_SPINES_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.spines_list.Find(features["spines"]), GLOB.spines_list.len)) + if(DNA_HUMAN_TAIL_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.tails_list_human.Find(features["tail_human"]), GLOB.tails_list_human.len)) + if(DNA_EARS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.ears_list.Find(features["ears"]), GLOB.ears_list.len)) + if(DNA_MOTH_WINGS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.moth_wings_list.Find(features["moth_wings"]), GLOB.moth_wings_list.len)) + if(DNA_MOTH_ANTENNAE_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.moth_antennae_list.Find(features["moth_antennae"]), GLOB.moth_antennae_list.len)) + if(DNA_MOTH_MARKINGS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.moth_markings_list.Find(features["moth_markings"]), GLOB.moth_markings_list.len)) + if(DNA_MUSHROOM_CAPS_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.caps_list.Find(features["caps"]), GLOB.caps_list.len)) + if(DNA_MONKEY_TAIL_BLOCK) + setblock(unique_features, blocknumber, construct_block(GLOB.tails_list_monkey.Find(features["tail_monkey"]), GLOB.tails_list_monkey.len)) //Please use add_mutation or activate_mutation instead /datum/dna/proc/force_give(datum/mutation/human/HM) @@ -223,7 +309,7 @@ * * target_dna The DNA that we are comparing to */ /datum/dna/proc/is_same_as(datum/dna/target_dna) - if(uni_identity == target_dna.uni_identity && mutation_index == target_dna.mutation_index && real_name == target_dna.real_name) + if(unique_identity == target_dna.unique_identity && mutation_index == target_dna.mutation_index && real_name == target_dna.real_name) if(species.type == target_dna.species.type && compare_list(features, target_dna.features) && blood_type == target_dna.blood_type) return TRUE return FALSE @@ -256,17 +342,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 @@ -341,6 +429,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 @@ -355,7 +444,7 @@ dna.blood_type = newblood_type if(ui) - dna.uni_identity = ui + dna.unique_identity = ui updateappearance(icon_update=0) if(LAZYLEN(mutation_index)) @@ -389,7 +478,7 @@ if(!has_dna()) return - switch(deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 3)) + switch(deconstruct_block(getblock(dna.unique_identity, DNA_GENDER_BLOCK), 3)) if(G_MALE) gender = MALE if(G_FEMALE) @@ -399,13 +488,51 @@ /mob/living/carbon/human/updateappearance(icon_update=1, mutcolor_update=0, mutations_overlay_update=0) ..() - var/structure = dna.uni_identity + var/structure = dna.unique_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_hairstyle = GLOB.facial_hairstyles_list[deconstruct_block(getblock(structure, DNA_FACIAL_HAIRSTYLE_BLOCK), GLOB.facial_hairstyles_list.len)] hairstyle = GLOB.hairstyles_list[deconstruct_block(getblock(structure, DNA_HAIRSTYLE_BLOCK), GLOB.hairstyles_list.len)] + var/features = dna.unique_features + if(dna.features["mcolor"]) + dna.features["mcolor"] = sanitize_hexcolor(getblock(features, DNA_MUTANT_COLOR_BLOCK)) + if(dna.features["ethcolor"]) + dna.features["ethcolor"] = sanitize_hexcolor(getblock(features, DNA_ETHEREAL_COLOR_BLOCK)) + if(dna.features["body_markings"]) + dna.features["body_markings"] = GLOB.body_markings_list[deconstruct_block(getblock(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(getblock(features, DNA_LIZARD_TAIL_BLOCK), GLOB.tails_list_lizard.len)] + if(dna.features["snout"]) + dna.features["snout"] = GLOB.snouts_list[deconstruct_block(getblock(features, DNA_SNOUT_BLOCK), GLOB.snouts_list.len)] + if(dna.features["horns"]) + dna.features["horns"] = GLOB.horns_list[deconstruct_block(getblock(features, DNA_HORNS_BLOCK), GLOB.horns_list.len)] + if(dna.features["frills"]) + dna.features["frills"] = GLOB.frills_list[deconstruct_block(getblock(features, DNA_FRILLS_BLOCK), GLOB.frills_list.len)] + if(dna.features["spines"]) + dna.features["spines"] = GLOB.spines_list[deconstruct_block(getblock(features, DNA_SPINES_BLOCK), GLOB.spines_list.len)] + if(dna.features["tail_human"]) + dna.features["tail_human"] = GLOB.tails_list_human[deconstruct_block(getblock(features, DNA_HUMAN_TAIL_BLOCK), GLOB.tails_list_human.len)] + if(dna.features["ears"]) + dna.features["ears"] = GLOB.ears_list[deconstruct_block(getblock(features, DNA_EARS_BLOCK), GLOB.ears_list.len)] + if(dna.features["moth_wings"]) + var/genetic_value = GLOB.moth_wings_list[deconstruct_block(getblock(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["moth_antennae"]) + var/genetic_value = GLOB.moth_antennae_list[deconstruct_block(getblock(features, DNA_MOTH_ANTENNAE_BLOCK), GLOB.moth_antennae_list.len)] + dna.features["original_moth_antennae"] = genetic_value + if(dna.features["moth_antennae"] != "Burnt Off") + dna.features["moth_antennae"] = genetic_value + if(dna.features["moth_markings"]) + dna.features["moth_markings"] = GLOB.moth_markings_list[deconstruct_block(getblock(features, DNA_MOTH_MARKINGS_BLOCK), GLOB.moth_markings_list.len)] + if(dna.features["caps"]) + dna.features["caps"] = GLOB.caps_list[deconstruct_block(getblock(features, DNA_MUSHROOM_CAPS_BLOCK), GLOB.caps_list.len)] + if(dna.features["tail_monkey"]) + dna.features["tail_monkey"] = GLOB.tails_list_monkey[deconstruct_block(getblock(features, DNA_MONKEY_TAIL_BLOCK), GLOB.tails_list_monkey.len)] + if(icon_update) 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() @@ -477,14 +604,14 @@ /proc/getleftblocks(input,blocknumber,blocksize) if(blocknumber > 1) - return copytext_char(input,1,((blocksize*blocknumber)-(blocksize-1))) + return copytext(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) + return copytext(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) + return copytext(input, blocksize*(blocknumber-1)+1, (blocksize*blocknumber)+1) /proc/setblock(istring, blocknumber, replacement, blocksize=DNA_BLOCK_SIZE) if(!istring || !blocknumber || !replacement || !blocksize) @@ -502,15 +629,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, resilient = NONE) +/mob/living/carbon/proc/easy_random_mutate(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE, resilient = NONE) if(!has_dna()) - return + CRASH("[src] does not have DNA") var/list/mutations = list() if(quality & POSITIVE) mutations += GLOB.good_mutations @@ -535,26 +662,34 @@ HM.mutadone_proof = 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 + var/newdna = setblock(dna.unique_identity, num, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) + dna.unique_identity = newdna 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) + var/newdna = setblock(dna.unique_features, num, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) + dna.unique_features = newdna + 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 FALSE + CRASH("[M] does not have DNA") if(se) for(var/i=1, i<=DNA_MUTATION_BLOCKS, i++) if(prob(probability)) @@ -563,9 +698,13 @@ if(ui) for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++) 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 TRUE + M.dna.unique_identity = setblock(M.dna.unique_identity, i, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) + if(uf) + for(var/i in 1 to DNA_FEATURE_BLOCKS) + if(prob(probability)) + M.dna.unique_features = setblock(M.dna.unique_features, i, random_string(DNA_BLOCK_SIZE, 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 diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index fbeb30dafaa..0af9fe6d2cc 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -56,7 +56,7 @@ var/list/prints = sniffed.return_fingerprints() if(prints) 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 6b9db21b3be..5c28eec9e0d 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 17dfaf85006..2db50521132 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -35,12 +35,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/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index 1b0c6dde760..2c72a07fa93 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -37,6 +37,9 @@ /// The base cooldown of the ability to copy enzymes and genetic makeup to people. #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." @@ -88,6 +91,8 @@ 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) @@ -144,7 +149,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 @@ -308,7 +313,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 @@ -510,12 +516,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() @@ -1163,8 +1169,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) @@ -1205,6 +1212,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") if(!COOLDOWN_FINISHED(src, enzyme_copy_timer)) @@ -1250,6 +1258,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()) @@ -1258,12 +1281,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 @@ -1286,7 +1309,8 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks - // "mixed" - Combination of both ue and ui + // "uf" - Unique Features, changes mutant bodyparts and mutcolors + // "mixed" - Combination of ue, ui, and uf if("makeup_apply") // GUARD CHECK - Can we genetically modify the occupant? Includes scanner // operational guard checks. @@ -1326,7 +1350,8 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks - // "mixed" - Combination of both ue and ui + // "uf" - Unique Features, changes mutant bodyparts and mutcolors + // "mixed" - Combination of ue, ui, and uf if("makeup_delay") // Convert the index to a number and clamp within the array range, then // copy the data from the disk to that buffer @@ -1349,6 +1374,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") @@ -1357,9 +1386,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() @@ -1585,11 +1621,24 @@ 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.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 @@ -1609,12 +1658,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 COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) - scanner_occupant.dna.uni_identity = buffer_slot["UI"] - scanner_occupant.updateappearance(mutations_overlay_update=1) + 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"] @@ -2101,20 +2151,38 @@ // 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/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index de411e38e2c..d5ccf441bd1 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -41,8 +41,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 @@ -484,10 +487,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 227b922e79b..7a2a91161ff 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -216,6 +216,7 @@ if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[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.")) diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm index 83e4bf43cc2..fdbf33dd777 100644 --- a/code/modules/admin/verbs/anonymousnames.dm +++ b/code/modules/admin/verbs/anonymousnames.dm @@ -93,11 +93,13 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme) if(issilicon(player)) player.fully_replace_character_name(player.real_name, theme.anonymous_ai_name(isAI(player))) else + var/mob/living/carbon/human/human_mob = player var/original_name = player.real_name //id will not be changed if you do not do this randomize_human(player) //do this first so the special name can be given player.fully_replace_character_name(original_name, theme.anonymous_name(player)) if(extras_enabled) player_extras(player) + human_mob.dna.update_dna_identity() /** * restore_all_players: sets all crewmembers on station back to their preference name. diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index ebd88fa0ecc..d3da9c0ef7d 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -140,7 +140,7 @@ for(var/i in GLOB.human_list) var/mob/living/carbon/human/H = i if(H.ckey) - dat += "