mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 17:52:36 +00:00
672 lines
23 KiB
Plaintext
672 lines
23 KiB
Plaintext
|
|
/////////////////////////// DNA DATUM
|
|
/datum/dna
|
|
var/unique_enzymes
|
|
var/uni_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
|
|
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
|
|
var/list/previous = list() //For temporary name/ui/ue/blood_type modifications
|
|
var/mob/living/holder
|
|
var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block
|
|
var/default_mutation_genes[DNA_MUTATION_BLOCKS] //List of the default genes from this mutation to allow DNA Scanner highlighting
|
|
var/stability = 100
|
|
var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers.
|
|
|
|
/datum/dna/New(mob/living/new_holder)
|
|
if(istype(new_holder))
|
|
holder = new_holder
|
|
|
|
/datum/dna/Destroy()
|
|
if(iscarbon(holder))
|
|
var/mob/living/carbon/cholder = holder
|
|
if(cholder.dna == src)
|
|
cholder.dna = null
|
|
holder = null
|
|
|
|
QDEL_NULL(species)
|
|
|
|
mutations.Cut() //This only references mutations, just dereference.
|
|
temporary_mutations.Cut() //^
|
|
previous.Cut() //^
|
|
|
|
return ..()
|
|
|
|
/datum/dna/proc/transfer_identity(mob/living/carbon/destination, transfer_SE = 0)
|
|
if(!istype(destination))
|
|
return
|
|
destination.dna.unique_enzymes = unique_enzymes
|
|
destination.dna.uni_identity = uni_identity
|
|
destination.dna.blood_type = blood_type
|
|
destination.set_species(species.type, icon_update=0)
|
|
destination.dna.features = features.Copy()
|
|
destination.dna.real_name = real_name
|
|
destination.dna.temporary_mutations = temporary_mutations.Copy()
|
|
if(transfer_SE)
|
|
destination.dna.mutation_index = mutation_index
|
|
destination.dna.default_mutation_genes = default_mutation_genes
|
|
|
|
/datum/dna/proc/copy_dna(datum/dna/new_dna)
|
|
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.blood_type = blood_type
|
|
new_dna.features = features.Copy()
|
|
new_dna.species = new species.type
|
|
new_dna.real_name = real_name
|
|
new_dna.mutations = mutations.Copy()
|
|
|
|
//See mutation.dm for what 'class' does. 'time' is time till it removes itself in decimals. 0 for no timer
|
|
/datum/dna/proc/add_mutation(mutation, class = MUT_OTHER, time)
|
|
var/mutation_type = mutation
|
|
if(istype(mutation, /datum/mutation/human))
|
|
var/datum/mutation/human/HM = mutation
|
|
mutation_type = HM.type
|
|
if(get_mutation(mutation_type))
|
|
return
|
|
SEND_SIGNAL(holder, COMSIG_CARBON_GAIN_MUTATION, mutation_type, class)
|
|
return force_give(new mutation_type (class, time, copymut = mutation))
|
|
|
|
/datum/dna/proc/remove_mutation(mutation_type)
|
|
SEND_SIGNAL(holder, COMSIG_CARBON_LOSE_MUTATION, mutation_type)
|
|
return force_lose(get_mutation(mutation_type))
|
|
|
|
/datum/dna/proc/check_mutation(mutation_type)
|
|
return get_mutation(mutation_type)
|
|
|
|
/datum/dna/proc/remove_all_mutations(list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
|
|
remove_mutation_group(mutations, classes, mutadone)
|
|
scrambled = FALSE
|
|
|
|
/datum/dna/proc/remove_mutation_group(list/group, list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
|
|
if(!group)
|
|
return
|
|
for(var/datum/mutation/human/HM in group)
|
|
if((HM.class in classes) && !(HM.mutadone_proof && mutadone))
|
|
force_lose(HM)
|
|
|
|
/datum/dna/proc/generate_uni_identity()
|
|
. = ""
|
|
var/list/L = new /list(DNA_UNI_IDENTITY_BLOCKS)
|
|
|
|
switch(holder.gender)
|
|
if(MALE)
|
|
L[DNA_GENDER_BLOCK] = construct_block(G_MALE, 3)
|
|
if(FEMALE)
|
|
L[DNA_GENDER_BLOCK] = construct_block(G_FEMALE, 3)
|
|
else
|
|
L[DNA_GENDER_BLOCK] = construct_block(G_PLURAL, 3)
|
|
if(ishuman(holder))
|
|
var/mob/living/carbon/human/H = holder
|
|
if(!GLOB.hairstyles_list.len)
|
|
init_sprite_accessory_subtypes(/datum/sprite_accessory/hair,GLOB.hairstyles_list, GLOB.hairstyles_male_list, GLOB.hairstyles_female_list)
|
|
L[DNA_HAIRSTYLE_BLOCK] = construct_block(GLOB.hairstyles_list.Find(H.hairstyle), GLOB.hairstyles_list.len)
|
|
L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color)
|
|
if(!GLOB.facial_hairstyles_list.len)
|
|
init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hairstyles_list, GLOB.facial_hairstyles_male_list, GLOB.facial_hairstyles_female_list)
|
|
L[DNA_FACIAL_HAIRSTYLE_BLOCK] = construct_block(GLOB.facial_hairstyles_list.Find(H.facial_hairstyle), GLOB.facial_hairstyles_list.len)
|
|
L[DNA_FACIAL_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.facial_hair_color)
|
|
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)
|
|
|
|
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 .
|
|
|
|
/datum/dna/proc/generate_dna_blocks()
|
|
var/bonus
|
|
if(species?.inert_mutation)
|
|
bonus = GET_INITIALIZED_MUTATION(species.inert_mutation)
|
|
var/list/mutations_temp = GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations + bonus
|
|
if(!LAZYLEN(mutations_temp))
|
|
return
|
|
mutation_index.Cut()
|
|
default_mutation_genes.Cut()
|
|
shuffle_inplace(mutations_temp)
|
|
mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE)
|
|
default_mutation_genes[RACEMUT] = mutation_index[RACEMUT]
|
|
for(var/i in 2 to DNA_MUTATION_BLOCKS)
|
|
var/datum/mutation/human/M = mutations_temp[i]
|
|
mutation_index[M.type] = create_sequence(M.type, FALSE, M.difficulty)
|
|
default_mutation_genes[M.type] = mutation_index[M.type]
|
|
shuffle_inplace(mutation_index)
|
|
|
|
//Used to generate original gene sequences for every mutation
|
|
/proc/generate_gene_sequence(length=4)
|
|
var/static/list/active_sequences = list("AT","TA","GC","CG")
|
|
var/sequence
|
|
for(var/i in 1 to length*DNA_SEQUENCE_LENGTH)
|
|
sequence += pick(active_sequences)
|
|
return sequence
|
|
|
|
//Used to create a chipped gene sequence
|
|
/proc/create_sequence(mutation, active, difficulty)
|
|
if(!difficulty)
|
|
var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation) //leaves the possibility to change difficulty mid-round
|
|
if(!A)
|
|
return
|
|
difficulty = A.difficulty
|
|
difficulty += rand(-2,4)
|
|
var/sequence = GET_SEQUENCE(mutation)
|
|
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)
|
|
difficulty--
|
|
return sequence
|
|
|
|
/datum/dna/proc/generate_unique_enzymes()
|
|
. = ""
|
|
if(istype(holder))
|
|
real_name = holder.real_name
|
|
. += md5(holder.real_name)
|
|
else
|
|
. += random_string(DNA_UNIQUE_ENZYMES_LEN, GLOB.hex_characters)
|
|
return .
|
|
|
|
/datum/dna/proc/update_ui_block(blocknumber)
|
|
if(!blocknumber || !ishuman(holder))
|
|
return
|
|
var/mob/living/carbon/human/H = holder
|
|
switch(blocknumber)
|
|
if(DNA_HAIR_COLOR_BLOCK)
|
|
setblock(uni_identity, blocknumber, sanitize_hexcolor(H.hair_color))
|
|
if(DNA_FACIAL_HAIR_COLOR_BLOCK)
|
|
setblock(uni_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))
|
|
if(DNA_EYE_COLOR_BLOCK)
|
|
setblock(uni_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))
|
|
if(FEMALE)
|
|
setblock(uni_identity, blocknumber, construct_block(G_FEMALE, 3))
|
|
else
|
|
setblock(uni_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))
|
|
if(DNA_HAIRSTYLE_BLOCK)
|
|
setblock(uni_identity, blocknumber, construct_block(GLOB.hairstyles_list.Find(H.hairstyle), GLOB.hairstyles_list.len))
|
|
|
|
//Please use add_mutation or activate_mutation instead
|
|
/datum/dna/proc/force_give(datum/mutation/human/HM)
|
|
if(holder && HM)
|
|
if(HM.class == MUT_NORMAL)
|
|
set_se(1, HM)
|
|
. = HM.on_acquiring(holder)
|
|
if(.)
|
|
qdel(HM)
|
|
update_instability()
|
|
|
|
//Use remove_mutation instead
|
|
/datum/dna/proc/force_lose(datum/mutation/human/HM)
|
|
if(holder && (HM in mutations))
|
|
set_se(0, HM)
|
|
. = HM.on_losing(holder)
|
|
update_instability(FALSE)
|
|
return
|
|
|
|
/**
|
|
* Checks if two DNAs are practically the same by comparing their most defining features
|
|
*
|
|
* Arguments:
|
|
* * 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(species.type == target_dna.species.type && compare_list(features, target_dna.features) && blood_type == target_dna.blood_type)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/dna/proc/update_instability(alert=TRUE)
|
|
stability = 100
|
|
for(var/datum/mutation/human/M in mutations)
|
|
if(M.class == MUT_EXTRA)
|
|
stability -= M.instability * GET_MUTATION_STABILIZER(M)
|
|
if(holder)
|
|
var/message
|
|
if(alert)
|
|
switch(stability)
|
|
if(70 to 90)
|
|
message = "<span class='warning'>You shiver.</span>"
|
|
if(60 to 69)
|
|
message = "<span class='warning'>You feel cold.</span>"
|
|
if(40 to 59)
|
|
message = "<span class='warning'>You feel sick.</span>"
|
|
if(20 to 39)
|
|
message = "<span class='warning'>It feels like your skin is moving.</span>"
|
|
if(1 to 19)
|
|
message = "<span class='warning'>You can feel your cells burning.</span>"
|
|
if(-INFINITY to 0)
|
|
message = "<span class='boldwarning'>You can feel your DNA exploding, we need to do something fast!</span>"
|
|
if(stability <= 0)
|
|
holder.apply_status_effect(STATUS_EFFECT_DNA_MELT)
|
|
if(message)
|
|
to_chat(holder, message)
|
|
|
|
//used to update dna UI, UE, and dna.real_name.
|
|
/datum/dna/proc/update_dna_identity()
|
|
uni_identity = generate_uni_identity()
|
|
unique_enzymes = generate_unique_enzymes()
|
|
|
|
/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()
|
|
if(!skip_index) //I hate this
|
|
generate_dna_blocks()
|
|
features = random_features()
|
|
|
|
|
|
/datum/dna/stored //subtype used by brain mob's stored_dna
|
|
|
|
/datum/dna/stored/add_mutation(mutation_name) //no mutation changes on stored dna.
|
|
return
|
|
|
|
/datum/dna/stored/remove_mutation(mutation_name)
|
|
return
|
|
|
|
/datum/dna/stored/check_mutation(mutation_name)
|
|
return
|
|
|
|
/datum/dna/stored/remove_all_mutations(list/classes, mutadone = FALSE)
|
|
return
|
|
|
|
/datum/dna/stored/remove_mutation_group(list/group)
|
|
return
|
|
|
|
/////////////////////////// DNA MOB-PROCS //////////////////////
|
|
|
|
/mob/proc/set_species(datum/species/mrace, icon_update = 1)
|
|
return
|
|
|
|
/mob/living/brain/set_species(datum/species/mrace, icon_update = 1)
|
|
if(mrace)
|
|
if(ispath(mrace))
|
|
stored_dna.species = new mrace()
|
|
else
|
|
stored_dna.species = mrace //not calling any species update procs since we're a brain, not a monkey/human
|
|
|
|
|
|
/mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
|
|
if(mrace && has_dna())
|
|
var/datum/species/new_race
|
|
if(ispath(mrace))
|
|
new_race = new mrace
|
|
else if(istype(mrace))
|
|
new_race = mrace
|
|
else
|
|
return
|
|
deathsound = new_race.deathsound
|
|
dna.species.on_species_loss(src, new_race, pref_load)
|
|
var/datum/species/old_species = dna.species
|
|
dna.species = new_race
|
|
dna.species.on_species_gain(src, old_species, pref_load)
|
|
if(ishuman(src))
|
|
qdel(language_holder)
|
|
var/species_holder = initial(mrace.species_language_holder)
|
|
language_holder = new species_holder(src)
|
|
update_atom_languages()
|
|
|
|
/mob/living/carbon/human/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
|
|
..()
|
|
if(icon_update)
|
|
update_body()
|
|
update_hair()
|
|
update_body_parts()
|
|
update_mutations_overlay()// no lizard with human hulk overlay please.
|
|
|
|
|
|
/mob/proc/has_dna()
|
|
return
|
|
|
|
/mob/living/carbon/has_dna()
|
|
return dna
|
|
|
|
|
|
/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, list/default_mutation_genes, newreal_name, newblood_type, datum/species/mrace, newfeatures, list/mutations, force_transfer_mutations)
|
|
//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
|
|
|
|
if(mrace)
|
|
var/datum/species/newrace = new mrace.type
|
|
newrace.copy_properties_from(mrace)
|
|
set_species(newrace, icon_update=0)
|
|
|
|
if(newreal_name)
|
|
dna.real_name = newreal_name
|
|
dna.generate_unique_enzymes()
|
|
|
|
if(newblood_type)
|
|
dna.blood_type = newblood_type
|
|
|
|
if(ui)
|
|
dna.uni_identity = ui
|
|
updateappearance(icon_update=0)
|
|
|
|
if(LAZYLEN(mutation_index))
|
|
dna.mutation_index = mutation_index.Copy()
|
|
if(LAZYLEN(default_mutation_genes))
|
|
dna.default_mutation_genes = default_mutation_genes.Copy()
|
|
else
|
|
dna.default_mutation_genes = mutation_index.Copy()
|
|
domutcheck()
|
|
|
|
if(mrace || newfeatures || ui)
|
|
update_body()
|
|
update_hair()
|
|
update_body_parts()
|
|
update_mutations_overlay()
|
|
|
|
if(LAZYLEN(mutations))
|
|
for(var/M in mutations)
|
|
var/datum/mutation/human/HM = M
|
|
if(HM.allow_transfer || force_transfer_mutations)
|
|
dna.force_give(new HM.type(HM.class, copymut=HM)) //using force_give since it may include exotic mutations that otherwise won't be handled properly
|
|
|
|
/mob/living/carbon/proc/create_dna()
|
|
dna = new /datum/dna(src)
|
|
if(!dna.species)
|
|
var/rando_race = pick(GLOB.roundstart_races)
|
|
dna.species = new rando_race()
|
|
|
|
//proc used to update the mob's appearance after its dna UI has been changed
|
|
/mob/living/carbon/proc/updateappearance(icon_update=1, mutcolor_update=0, mutations_overlay_update=0)
|
|
if(!has_dna())
|
|
return
|
|
|
|
switch(deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 3))
|
|
if(G_MALE)
|
|
gender = MALE
|
|
if(G_FEMALE)
|
|
gender = FEMALE
|
|
else
|
|
gender = PLURAL
|
|
|
|
/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_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)]
|
|
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()
|
|
if(mutcolor_update)
|
|
update_body_parts()
|
|
if(mutations_overlay_update)
|
|
update_mutations_overlay()
|
|
|
|
|
|
/mob/proc/domutcheck()
|
|
return
|
|
|
|
/mob/living/carbon/domutcheck()
|
|
if(!has_dna())
|
|
return
|
|
|
|
for(var/mutation in dna.mutation_index)
|
|
dna.check_block(mutation)
|
|
|
|
update_mutations_overlay()
|
|
|
|
/datum/dna/proc/check_block(mutation)
|
|
var/datum/mutation/human/HM = get_mutation(mutation)
|
|
if(check_block_string(mutation))
|
|
if(!HM)
|
|
. = add_mutation(mutation, MUT_NORMAL)
|
|
return
|
|
return force_lose(HM)
|
|
|
|
//Return the active mutation of a type if there is one
|
|
/datum/dna/proc/get_mutation(A)
|
|
for(var/datum/mutation/human/HM in mutations)
|
|
if(HM.type == A)
|
|
return HM
|
|
|
|
/datum/dna/proc/check_block_string(mutation)
|
|
if((LAZYLEN(mutation_index) > DNA_MUTATION_BLOCKS) || !(mutation in mutation_index))
|
|
return FALSE
|
|
return is_gene_active(mutation)
|
|
|
|
/datum/dna/proc/is_gene_active(mutation)
|
|
return (mutation_index[mutation] == GET_SEQUENCE(mutation))
|
|
|
|
/datum/dna/proc/set_se(on=TRUE, datum/mutation/human/HM)
|
|
if(!HM || !(HM.type in mutation_index) || (LAZYLEN(mutation_index) < DNA_MUTATION_BLOCKS))
|
|
return
|
|
. = TRUE
|
|
if(on)
|
|
mutation_index[HM.type] = GET_SEQUENCE(HM.type)
|
|
default_mutation_genes[HM.type] = mutation_index[HM.type]
|
|
else if(GET_SEQUENCE(HM.type) == mutation_index[HM.type])
|
|
mutation_index[HM.type] = create_sequence(HM.type, FALSE, HM.difficulty)
|
|
default_mutation_genes[HM.type] = mutation_index[HM.type]
|
|
|
|
|
|
/datum/dna/proc/activate_mutation(mutation) //note that this returns a boolean and not a new mob
|
|
if(!mutation)
|
|
return FALSE
|
|
var/mutation_type = mutation
|
|
if(istype(mutation, /datum/mutation/human))
|
|
var/datum/mutation/human/M = mutation
|
|
mutation_type = M.type
|
|
if(!mutation_in_sequence(mutation_type)) //cant activate what we dont have, use add_mutation
|
|
return FALSE
|
|
add_mutation(mutation, MUT_NORMAL)
|
|
return TRUE
|
|
|
|
/////////////////////////// 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
|
|
if(istype(mutation, /datum/mutation/human))
|
|
var/datum/mutation/human/HM = mutation
|
|
if(HM.type in mutation_index)
|
|
return TRUE
|
|
else if(mutation in mutation_index)
|
|
return TRUE
|
|
|
|
|
|
/mob/living/carbon/proc/randmut(list/candidates, difficulty = 2)
|
|
if(!has_dna())
|
|
return
|
|
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)
|
|
if(!has_dna())
|
|
return
|
|
var/list/mutations = list()
|
|
if(quality & POSITIVE)
|
|
mutations += GLOB.good_mutations
|
|
if(quality & NEGATIVE)
|
|
mutations += GLOB.bad_mutations
|
|
if(quality & MINOR_NEGATIVE)
|
|
mutations += GLOB.not_good_mutations
|
|
var/list/possible = list()
|
|
for(var/datum/mutation/human/A in mutations)
|
|
if((!sequence || dna.mutation_in_sequence(A.type)) && !dna.get_mutation(A.type))
|
|
possible += A.type
|
|
if(exclude_monkey)
|
|
possible.Remove(RACEMUT)
|
|
if(LAZYLEN(possible))
|
|
var/mutation = pick(possible)
|
|
. = dna.activate_mutation(mutation)
|
|
if(scrambled)
|
|
var/datum/mutation/human/HM = dna.get_mutation(mutation)
|
|
if(HM)
|
|
HM.scrambled = TRUE
|
|
if(HM.quality & resilient)
|
|
HM.mutadone_proof = TRUE
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/randmuti()
|
|
if(!has_dna())
|
|
return
|
|
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
|
|
updateappearance(mutations_overlay_update=1)
|
|
|
|
/mob/living/carbon/proc/clean_dna()
|
|
if(!has_dna())
|
|
return
|
|
dna.remove_all_mutations()
|
|
|
|
/mob/living/carbon/proc/clean_randmut(list/candidates, difficulty = 2)
|
|
clean_dna()
|
|
randmut(candidates, difficulty)
|
|
|
|
/proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, probability)
|
|
if(!M.has_dna())
|
|
return FALSE
|
|
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++)
|
|
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
|
|
|
|
//value in range 1 to values. values must be greater than 0
|
|
//all arguments assumed to be positive integers
|
|
/proc/construct_block(value, values, blocksize=DNA_BLOCK_SIZE)
|
|
var/width = round((16**blocksize)/values)
|
|
if(value < 1)
|
|
value = 1
|
|
value = (value * width) - rand(1,width)
|
|
return num2hex(value, blocksize)
|
|
|
|
//value is hex
|
|
/proc/deconstruct_block(value, values, blocksize=DNA_BLOCK_SIZE)
|
|
var/width = round((16**blocksize)/values)
|
|
value = round(hex2num(value) / width) + 1
|
|
if(value > values)
|
|
value = values
|
|
return value
|
|
|
|
/////////////////////////// DNA HELPER-PROCS
|
|
|
|
/mob/living/carbon/human/proc/something_horrible(ignore_stability)
|
|
if(!has_dna()) //shouldn't ever happen anyway so it's just in really weird cases
|
|
return
|
|
if(!ignore_stability && (dna.stability > 0))
|
|
return
|
|
var/instability = -dna.stability
|
|
dna.remove_all_mutations()
|
|
dna.stability = 100
|
|
if(prob(max(70-instability,0)))
|
|
switch(rand(0,10)) //not complete and utter death
|
|
if(0)
|
|
monkeyize()
|
|
if(1)
|
|
gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic)
|
|
new/obj/vehicle/ridden/wheelchair(get_turf(src)) //don't buckle, because I can't imagine to plethora of things to go through that could otherwise break
|
|
to_chat(src, "<span class='warning'>My flesh turned into a wheelchair and I can't feel my legs.</span>")
|
|
if(2)
|
|
corgize()
|
|
if(3)
|
|
to_chat(src, "<span class='notice'>Oh, I actually feel quite alright!</span>")
|
|
if(4)
|
|
to_chat(src, "<span class='notice'>Oh, I actually feel quite alright!</span>") //you thought
|
|
physiology.damage_resistance = -20000
|
|
if(5)
|
|
to_chat(src, "<span class='notice'>Oh, I actually feel quite alright!</span>")
|
|
reagents.add_reagent(/datum/reagent/aslimetoxin, 10)
|
|
if(6)
|
|
apply_status_effect(STATUS_EFFECT_GO_AWAY)
|
|
if(7)
|
|
to_chat(src, "<span class='notice'>Oh, I actually feel quite alright!</span>")
|
|
ForceContractDisease(new/datum/disease/decloning()) //slow acting, non-viral clone damage based GBS
|
|
if(8)
|
|
var/list/elligible_organs = list()
|
|
for(var/obj/item/organ/O in internal_organs) //make sure we dont get an implant or cavity item
|
|
elligible_organs += O
|
|
vomit(20, TRUE)
|
|
if(elligible_organs.len)
|
|
var/obj/item/organ/O = pick(elligible_organs)
|
|
O.Remove(src)
|
|
visible_message("<span class='danger'>[src] vomits up their [O.name]!</span>", "<span class='danger'>You vomit up your [O.name]</span>") //no "vomit up your heart"
|
|
O.forceMove(drop_location())
|
|
if(prob(20))
|
|
O.animate_atom_living()
|
|
if(9 to 10)
|
|
ForceContractDisease(new/datum/disease/gastrolosis())
|
|
to_chat(src, "<span class='notice'>Oh, I actually feel quite alright!</span>")
|
|
else
|
|
switch(rand(0,5))
|
|
if(0)
|
|
gib()
|
|
if(1)
|
|
dust()
|
|
|
|
if(2)
|
|
death()
|
|
petrify(INFINITY)
|
|
if(3)
|
|
if(prob(95))
|
|
var/obj/item/bodypart/BP = get_bodypart(pick(BODY_ZONE_CHEST,BODY_ZONE_HEAD))
|
|
if(BP)
|
|
BP.dismember()
|
|
else
|
|
gib()
|
|
else
|
|
set_species(/datum/species/dullahan)
|
|
if(4)
|
|
visible_message("<span class='warning'>[src]'s skin melts off!</span>", "<span class='boldwarning'>Your skin melts off!</span>")
|
|
spawn_gibs()
|
|
set_species(/datum/species/skeleton)
|
|
if(prob(90))
|
|
addtimer(CALLBACK(src, .proc/death), 30)
|
|
if(5)
|
|
to_chat(src, "<span class='phobia'>LOOK UP!</span>")
|
|
addtimer(CALLBACK(src, .proc/something_horrible_mindmelt), 30)
|
|
|
|
|
|
/mob/living/carbon/human/proc/something_horrible_mindmelt()
|
|
if(!is_blind())
|
|
var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in internal_organs
|
|
if(!eyes)
|
|
return
|
|
eyes.Remove(src)
|
|
qdel(eyes)
|
|
visible_message("<span class='notice'>[src] looks up and their eyes melt away!</span>", "<span class>='userdanger'>I understand now.</span>")
|
|
addtimer(CALLBACK(src, .proc/adjustOrganLoss, ORGAN_SLOT_BRAIN, 200), 20)
|