Datumizes DNA blocks, makes DNA cleaner in general (#92061)

## About The Pull Request

Moves all the dna block handling onto singleton datums initialized
inside global lists, to make the handling dna less of a copy-paste mess
and make adding new blocks significantly easier. There is still some
work to be done in the copypaste department but ultimately that falls
under its own PR scope after the core refactor goes through. (Ill
probably do those but it will also be easier for everyone else as the
code is now significantly less of an eyesore)

Both features and identities have been tested through and through, and
seem to be working fine.

Also removed the reliance on weird hardcoded lookup tables for length,
and other similar things that just didn't make sense when I was passing
through DNA code. There's a lot more that fall out of scope for this
exact PR's goal however

## Why It's Good For The Game

I've been told the maintainers will love me for doing this

## Changelog

🆑
code: feature keys are no longer magical strings floating around the
codebase and use proper defines
refactor: DNA blocks are now handled with singleton datums.
/🆑
This commit is contained in:
Waterpig
2025-07-15 00:51:45 +02:00
committed by GitHub
parent c4ee3d604f
commit b01756b97c
52 changed files with 622 additions and 492 deletions

View File

@@ -44,55 +44,40 @@
//DNA - Because fuck you and your magic numbers being all over the codebase.
#define DNA_BLOCK_SIZE 3
#define DNA_BLOCK_SIZE_COLOR DEFAULT_HEX_COLOR_LEN
#define DNA_GENDER_BLOCK 1
#define DNA_SKIN_TONE_BLOCK 2
#define DNA_EYE_COLOR_LEFT_BLOCK 3
#define DNA_EYE_COLOR_RIGHT_BLOCK 4
#define DNA_HAIRSTYLE_BLOCK 5
#define DNA_HAIR_COLOR_BLOCK 6
#define DNA_FACIAL_HAIRSTYLE_BLOCK 7
#define DNA_FACIAL_HAIR_COLOR_BLOCK 8
#define DNA_HAIRSTYLE_GRADIENT_BLOCK 9
#define DNA_HAIR_COLOR_GRADIENT_BLOCK 10
#define DNA_FACIAL_HAIRSTYLE_GRADIENT_BLOCK 11
#define DNA_FACIAL_HAIR_COLOR_GRADIENT_BLOCK 12
#define DNA_UNI_IDENTITY_BLOCKS 12
/// This number needs to equal the total number of DNA blocks
#define DNA_MUTANT_COLOR_BLOCK 1
#define DNA_ETHEREAL_COLOR_BLOCK 2
#define DNA_LIZARD_MARKINGS_BLOCK 3
#define DNA_TAIL_BLOCK 4
#define DNA_LIZARD_TAIL_BLOCK 5
#define DNA_SNOUT_BLOCK 6
#define DNA_HORNS_BLOCK 7
#define DNA_FRILLS_BLOCK 8
#define DNA_SPINES_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_POD_HAIR_BLOCK 15
#define DNA_FISH_TAIL_BLOCK 16
// Hey! Listen up if you're here because you're adding a species feature!
//
// You don't need to add a DNA block for EVERY species feature!
// You ONLY need DNA blocks if you intend to allow players to change it via GENETICS!
// (Which means having a DNA block for a feature tied to a mob without DNA is entirely pointless.)
/// Total amount of DNA blocks, must be equal to the highest DNA block number
#define DNA_FEATURE_BLOCKS 16
#define DNA_SEQUENCE_LENGTH 4
#define DNA_MUTATION_BLOCKS 8
#define DNA_UNIQUE_ENZYMES_LEN 32
//Features - No more magic strings
//These can't just simply use dna block paths like identities, because there's keys that aren't tied to blocks
// Block tied
#define FEATURE_MUTANT_COLOR "mcolor"
#define FEATURE_ETHEREAL_COLOR "ethcolor"
#define FEATURE_EARS "ears"
#define FEATURE_TAIL "tail_cat"
#define FEATURE_TAIL_LIZARD "tail_lizard"
#define FEATURE_TAIL_FISH "tail_fish"
#define FEATURE_SNOUT "snout"
#define FEATURE_LIZARD_MARKINGS "marking_lizard"
#define FEATURE_HORNS "horns"
#define FEATURE_FRILLS "frills"
#define FEATURE_SPINES "spines"
#define FEATURE_MOTH_WINGS "moth_wings"
#define FEATURE_MOTH_ANTENNAE "moth_antennae"
#define FEATURE_MOTH_MARKINGS "moth_markings"
#define FEATURE_MUSH_CAP "caps"
#define FEATURE_POD_HAIR "pod_hair"
// Other
#define FEATURE_WINGS "wings"
#define FEATURE_TAIL_MONKEY "tail_monkey"
#define FEATURE_TAIL_XENO "tail_xeno"
#define FEATURE_TAILSPINES "tailspines" // Different from regular spines, these appear on tails
#define FEATURE_LEGS "legs"
///flag for the transfer_flag argument from dna/proc/copy_dna(). This one makes it so the SE is copied too.
#define COPY_DNA_SE (1<<0)
///flag for the transfer_flag argument from dna/proc/copy_dna(). This one copies the species.

View File

@@ -12,7 +12,5 @@
#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)
///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)

View File

@@ -197,3 +197,21 @@ GLOBAL_LIST_INIT(construct_radial_images, list(
if(mind)
minds += mind
return minds
/// A keyed list of identity block singletons, in a key:value group of typepath:block
GLOBAL_LIST_INIT(dna_identity_blocks, init_identity_block_types())
/proc/init_identity_block_types()
. = list()
for(var/datum/dna_block/identity/block_path as anything in subtypesof(/datum/dna_block/identity))
var/datum/dna_block/identity/new_block = new block_path()
.[block_path] = new_block
/// A keyed list of feature block singletons, in a key:value group of typepath:block
GLOBAL_LIST_INIT(dna_feature_blocks, init_feature_block_types())
/proc/init_feature_block_types()
. = list()
for(var/datum/dna_block/feature/block_path as anything in subtypesof(/datum/dna_block/feature))
var/datum/dna_block/feature/new_block = new block_path()
.[block_path] = new_block

View File

@@ -11,6 +11,7 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
name = "Sprite Accessories"
flags = SS_NO_FIRE | SS_NO_INIT
// HOLY SHIT COMPACT THIS INTO ASSOCIATED LISTS SO WE STOP ADDING VARIABLES
//Hairstyles
var/list/hairstyles_list //! stores /datum/sprite_accessory/hair indexed by name
var/list/hairstyles_male_list //! stores only hair names

View File

@@ -43,13 +43,13 @@
return mutable_appearance(icon, gender_string + icon_state + "_" + limb.body_zone, layer = layer)
/datum/bodypart_overlay/simple/body_marking/moth
dna_feature_key = "moth_markings"
dna_feature_key = FEATURE_MOTH_MARKINGS
/datum/bodypart_overlay/simple/body_marking/moth/get_accessory(name)
return SSaccessories.moth_markings_list[name]
/datum/bodypart_overlay/simple/body_marking/lizard
dna_feature_key = "lizard_markings"
dna_feature_key = FEATURE_LIZARD_MARKINGS
applies_to = list(/obj/item/bodypart/chest)
/datum/bodypart_overlay/simple/body_marking/lizard/get_accessory(name)

View File

@@ -0,0 +1,151 @@
/datum/dna_block/feature/mutant_color
block_length = DNA_BLOCK_SIZE_COLOR
feature_key = FEATURE_MUTANT_COLOR
/datum/dna_block/feature/mutant_color/create_unique_block(mob/living/carbon/human/target)
return sanitize_hexcolor(target.dna.features[feature_key], include_crunch = FALSE)
/datum/dna_block/feature/mutant_color/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = sanitize_hexcolor(get_block(dna_hash))
/datum/dna_block/feature/ethereal_color
block_length = DNA_BLOCK_SIZE_COLOR
feature_key = FEATURE_ETHEREAL_COLOR
/datum/dna_block/feature/ethereal_color/create_unique_block(mob/living/carbon/human/target)
return sanitize_hexcolor(target.dna.features[FEATURE_ETHEREAL_COLOR], include_crunch = FALSE)
/datum/dna_block/feature/ethereal_color/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = sanitize_hexcolor(get_block(dna_hash))
/datum/dna_block/feature/ears
feature_key = FEATURE_EARS
/datum/dna_block/feature/ears/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.ears_list.Find(target.dna.features[feature_key]), length(SSaccessories.ears_list))
/datum/dna_block/feature/ears/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.ears_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.ears_list))]
// One day, someone should consider merging all tails into one, this is stupid
// No I don't care that it will "Create situations where a felinid grows a lizard tail" that makes it more fun
/datum/dna_block/feature/tail
feature_key = FEATURE_TAIL
/datum/dna_block/feature/tail/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.tails_list_felinid.Find(target.dna.features[feature_key]), length(SSaccessories.tails_list_felinid))
/datum/dna_block/feature/tail/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.tails_list_felinid[deconstruct_block(get_block(dna_hash), length(SSaccessories.tails_list_felinid))]
/datum/dna_block/feature/tail_lizard
feature_key = FEATURE_TAIL_LIZARD
/datum/dna_block/feature/tail_lizard/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.tails_list_lizard.Find(target.dna.features[feature_key]), length(SSaccessories.tails_list_lizard))
/datum/dna_block/feature/tail_lizard/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.tails_list_lizard[deconstruct_block(get_block(dna_hash), length(SSaccessories.tails_list_lizard))]
/datum/dna_block/feature/tail_fish
feature_key = FEATURE_TAIL_FISH
/datum/dna_block/feature/tail_fish/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.tails_list_fish.Find(target.dna.features[feature_key]), length(SSaccessories.tails_list_fish))
/datum/dna_block/feature/tail_fish/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.tails_list_fish[deconstruct_block(get_block(dna_hash), length(SSaccessories.tails_list_fish))]
/datum/dna_block/feature/snout
feature_key = FEATURE_SNOUT
/datum/dna_block/feature/snout/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.snouts_list.Find(target.dna.features[feature_key]), length(SSaccessories.snouts_list))
/datum/dna_block/feature/snout/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.snouts_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.snouts_list))]
/datum/dna_block/feature/lizard_marking
feature_key = FEATURE_LIZARD_MARKINGS
/datum/dna_block/feature/lizard_marking/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.lizard_markings_list.Find(target.dna.features[feature_key]), length(SSaccessories.lizard_markings_list))
/datum/dna_block/feature/lizard_marking/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.lizard_markings_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.lizard_markings_list))]
/datum/dna_block/feature/horn
feature_key = FEATURE_HORNS
/datum/dna_block/feature/horn/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.horns_list.Find(target.dna.features[feature_key]), length(SSaccessories.horns_list))
/datum/dna_block/feature/horn/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.horns_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.horns_list))]
/datum/dna_block/feature/frill
feature_key = FEATURE_FRILLS
/datum/dna_block/feature/frill/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.frills_list.Find(target.dna.features[feature_key]), length(SSaccessories.frills_list))
/datum/dna_block/feature/frill/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.frills_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.frills_list))]
/datum/dna_block/feature/spine
feature_key = FEATURE_SPINES
/datum/dna_block/feature/spine/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.spines_list.Find(target.dna.features[feature_key]), length(SSaccessories.spines_list))
/datum/dna_block/feature/spine/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.spines_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.spines_list))]
/datum/dna_block/feature/moth_wing
feature_key = FEATURE_MOTH_WINGS
/datum/dna_block/feature/moth_wing/create_unique_block(mob/living/carbon/human/target)
if(target.dna.features[feature_key] == "Burnt Off") // Why is this snowflake check a thing. Please find a way to fix this later
return random_string(block_length, GLOB.hex_characters)
return construct_block(SSaccessories.moth_wings_list.Find(target.dna.features[feature_key]), length(SSaccessories.moth_wings_list))
/datum/dna_block/feature/moth_wing/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.moth_wings_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.moth_wings_list))]
/datum/dna_block/feature/moth_antenna
feature_key = FEATURE_MOTH_ANTENNAE
/datum/dna_block/feature/moth_antenna/create_unique_block(mob/living/carbon/human/target)
if(target.dna.features[feature_key] == "Burnt Off")
return random_string(block_length, GLOB.hex_characters)
return construct_block(SSaccessories.moth_antennae_list.Find(target.dna.features[feature_key]), length(SSaccessories.moth_antennae_list))
/datum/dna_block/feature/moth_antenna/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.moth_antennae_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.moth_antennae_list))]
/datum/dna_block/feature/moth_marking
feature_key = FEATURE_MOTH_MARKINGS
/datum/dna_block/feature/moth_marking/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.moth_markings_list.Find(target.dna.features[feature_key]), length(SSaccessories.moth_markings_list))
/datum/dna_block/feature/moth_marking/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.moth_markings_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.moth_markings_list))]
/datum/dna_block/feature/mush_cap
feature_key = FEATURE_MUSH_CAP
/datum/dna_block/feature/mush_cap/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.caps_list.Find(target.dna.features[feature_key]), length(SSaccessories.caps_list))
/datum/dna_block/feature/mush_cap/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.caps_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.caps_list))]
/datum/dna_block/feature/pod_hair
feature_key = FEATURE_POD_HAIR
/datum/dna_block/feature/pod_hair/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.pod_hair_list.Find(target.dna.features[feature_key]), length(SSaccessories.pod_hair_list))
/datum/dna_block/feature/pod_hair/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.dna.features[feature_key] = SSaccessories.pod_hair_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.pod_hair_list))]

View File

@@ -0,0 +1,129 @@
/datum/dna_block/identity/gender
/datum/dna_block/identity/gender/create_unique_block(mob/living/carbon/human/target)
//ignores TRAIT_AGENDER so that a "real" gender can be stored in the DNA if later use is needed
switch(target.gender)
if(MALE)
. = construct_block(G_MALE, GENDERS)
if(FEMALE)
. = construct_block(G_FEMALE, GENDERS)
if(NEUTER)
. = construct_block(G_NEUTER, GENDERS)
else
. = construct_block(G_PLURAL, GENDERS)
return .
/datum/dna_block/identity/gender/apply_to_mob(mob/living/carbon/human/target, dna_hash)
//Always plural gender if agender
if(HAS_TRAIT(target, TRAIT_AGENDER))
target.gender = PLURAL
return
switch(deconstruct_block(get_block(dna_hash), GENDERS))
if(G_MALE)
target.gender = MALE
if(G_FEMALE)
target.gender = FEMALE
if(G_NEUTER)
target.gender = NEUTER
else
target.gender = PLURAL
/datum/dna_block/identity/skin_tone
/datum/dna_block/identity/skin_tone/create_unique_block(mob/living/carbon/human/target)
return construct_block(GLOB.skin_tones.Find(target.skin_tone), GLOB.skin_tones.len)
/datum/dna_block/identity/skin_tone/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.skin_tone = GLOB.skin_tones[deconstruct_block(get_block(dna_hash), GLOB.skin_tones.len)]
/// Holds both the left and right eye color at once
/datum/dna_block/identity/eye_colors
block_length = DNA_BLOCK_SIZE_COLOR * 2 // Left eye color, then right eye color
/datum/dna_block/identity/eye_colors/create_unique_block(mob/living/carbon/human/target)
var/left = sanitize_hexcolor(target.eye_color_left, include_crunch = FALSE)
var/right = sanitize_hexcolor(target.eye_color_right, include_crunch = FALSE)
return left + right
/datum/dna_block/identity/eye_colors/apply_to_mob(mob/living/carbon/human/target, dna_hash)
var/colors = get_block(dna_hash)
var/right_color_begin = DNA_BLOCK_SIZE_COLOR + 1
target.set_eye_color(sanitize_hexcolor(copytext(colors, 1, right_color_begin)), sanitize_hexcolor(copytext(colors, right_color_begin, length(colors) + 1)))
/datum/dna_block/identity/hair_style
/datum/dna_block/identity/hair_style/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.hairstyles_list.Find(target.hairstyle), length(SSaccessories.hairstyles_list))
/datum/dna_block/identity/hair_style/apply_to_mob(mob/living/carbon/human/target, dna_hash)
if(HAS_TRAIT(target, TRAIT_BALD))
target.set_hairstyle("Bald", update = FALSE)
return
var/style = SSaccessories.hairstyles_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.hairstyles_list))]
target.set_hairstyle(style, update = FALSE)
/datum/dna_block/identity/hair_color
block_length = DNA_BLOCK_SIZE_COLOR
/datum/dna_block/identity/hair_color/create_unique_block(mob/living/carbon/human/target)
return sanitize_hexcolor(target.hair_color, include_crunch = FALSE)
/datum/dna_block/identity/hair_color/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.set_haircolor(sanitize_hexcolor(get_block(dna_hash)), update = FALSE)
/datum/dna_block/identity/facial_style
/datum/dna_block/identity/facial_style/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.facial_hairstyles_list.Find(target.facial_hairstyle), length(SSaccessories.facial_hairstyles_list))
/datum/dna_block/identity/facial_style/apply_to_mob(mob/living/carbon/human/target, dna_hash)
if(HAS_TRAIT(src, TRAIT_SHAVED))
target.set_facial_hairstyle("Shaved", update = FALSE)
return
var/style = SSaccessories.facial_hairstyles_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.facial_hairstyles_list))]
target.set_facial_hairstyle(style, update = FALSE)
/datum/dna_block/identity/facial_color
block_length = DNA_BLOCK_SIZE_COLOR
/datum/dna_block/identity/facial_color/create_unique_block(mob/living/carbon/human/target)
return sanitize_hexcolor(target.facial_hair_color, include_crunch = FALSE)
/datum/dna_block/identity/facial_color/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.set_facial_haircolor(sanitize_hexcolor(get_block(dna_hash)), update = FALSE)
/datum/dna_block/identity/hair_gradient
/datum/dna_block/identity/hair_gradient/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.hair_gradients_list.Find(target.grad_style[GRADIENT_HAIR_KEY]), length(SSaccessories.hair_gradients_list))
/datum/dna_block/identity/hair_gradient/apply_to_mob(mob/living/carbon/human/target, dna_hash)
var/gradient_style = SSaccessories.hair_gradients_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.hair_gradients_list))]
target.set_hair_gradient_style(gradient_style, update = FALSE)
/datum/dna_block/identity/hair_gradient_color
block_length = DNA_BLOCK_SIZE_COLOR
/datum/dna_block/identity/hair_gradient_color/create_unique_block(mob/living/carbon/human/target)
return sanitize_hexcolor(target.grad_color[GRADIENT_HAIR_KEY], include_crunch = FALSE)
/datum/dna_block/identity/hair_gradient_color/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.set_hair_gradient_color(sanitize_hexcolor(get_block(dna_hash)), update = FALSE)
/datum/dna_block/identity/facial_gradient
/datum/dna_block/identity/facial_gradient/create_unique_block(mob/living/carbon/human/target)
return construct_block(SSaccessories.facial_hair_gradients_list.Find(target.grad_style[GRADIENT_FACIAL_HAIR_KEY]), length(SSaccessories.facial_hair_gradients_list))
/datum/dna_block/identity/facial_gradient/apply_to_mob(mob/living/carbon/human/target, dna_hash)
var/gradient_style = SSaccessories.hair_gradients_list[deconstruct_block(get_block(dna_hash), length(SSaccessories.hair_gradients_list))]
target.set_facial_hair_gradient_style(gradient_style, update = FALSE)
/datum/dna_block/identity/facial_gradient_color
block_length = DNA_BLOCK_SIZE_COLOR
/datum/dna_block/identity/facial_gradient_color/create_unique_block(mob/living/carbon/human/target)
return sanitize_hexcolor(target.grad_color[GRADIENT_FACIAL_HAIR_KEY], include_crunch = FALSE)
/datum/dna_block/identity/facial_gradient_color/apply_to_mob(mob/living/carbon/human/target, dna_hash)
target.set_facial_hair_gradient_color(sanitize_hexcolor(get_block(dna_hash)), update = FALSE)

View File

@@ -1,31 +1,3 @@
/**
* 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_LEFT_BLOCK]" = DNA_BLOCK_SIZE_COLOR,
"[DNA_EYE_COLOR_RIGHT_BLOCK]" = DNA_BLOCK_SIZE_COLOR,
"[DNA_HAIR_COLOR_GRADIENT_BLOCK]" = DNA_BLOCK_SIZE_COLOR,
"[DNA_FACIAL_HAIR_COLOR_GRADIENT_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.
@@ -37,9 +9,10 @@ GLOBAL_LIST_INIT(standard_mutation_sources, list(MUTATION_SOURCE_ACTIVATED, MUTA
/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)
for(var/block_path in GLOB.dna_identity_blocks)
var/datum/dna_block/identity/block = GLOB.dna_identity_blocks[block_path]
.[block_path] += total_block_len
total_block_len += block.block_length
///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())
@@ -47,9 +20,10 @@ 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)
for(var/block_path in GLOB.dna_feature_blocks)
var/datum/dna_block/feature/block = GLOB.dna_feature_blocks[block_path]
.[block_path] += total_block_len
total_block_len += block.block_length
/////////////////////////// DNA DATUM
/datum/dna
@@ -63,7 +37,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
var/datum/species/species = new /datum/species/human
/// Assoc list of feature keys to their value
/// Note if you set these manually, and do not update [unique_features] afterwards, it will likely be reset.
var/list/features = list("mcolor" = COLOR_WHITE)
var/list/features = list(FEATURE_MUTANT_COLOR = COLOR_WHITE)
///Stores the hashed values of the person's non-human features
var/unique_features
///Stores the real name of the person who originally got this dna datum. Used primarily for changelings
@@ -211,77 +185,18 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
/datum/dna/proc/generate_unique_identity()
. = ""
var/list/L = new /list(DNA_UNI_IDENTITY_BLOCKS)
//ignores TRAIT_AGENDER so that a "real" gender can be stored in the DNA if later use is needed
switch(holder.gender)
if(MALE)
L[DNA_GENDER_BLOCK] = construct_block(G_MALE, GENDERS)
if(FEMALE)
L[DNA_GENDER_BLOCK] = construct_block(G_FEMALE, GENDERS)
if(NEUTER)
L[DNA_GENDER_BLOCK] = construct_block(G_NEUTER, GENDERS)
else
L[DNA_GENDER_BLOCK] = construct_block(G_PLURAL, GENDERS)
if(ishuman(holder))
var/mob/living/carbon/human/H = holder
if(length(SSaccessories.hairstyles_list) == 0 || length(SSaccessories.facial_hairstyles_list) == 0)
CRASH("SSaccessories lists are empty, this is bad!")
L[DNA_HAIRSTYLE_BLOCK] = construct_block(SSaccessories.hairstyles_list.Find(H.hairstyle), length(SSaccessories.hairstyles_list))
L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color, include_crunch = FALSE)
L[DNA_FACIAL_HAIRSTYLE_BLOCK] = construct_block(SSaccessories.facial_hairstyles_list.Find(H.facial_hairstyle), length(SSaccessories.facial_hairstyles_list))
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_LEFT_BLOCK] = sanitize_hexcolor(H.eye_color_left, include_crunch = FALSE)
L[DNA_EYE_COLOR_RIGHT_BLOCK] = sanitize_hexcolor(H.eye_color_right, include_crunch = FALSE)
L[DNA_HAIRSTYLE_GRADIENT_BLOCK] = construct_block(SSaccessories.hair_gradients_list.Find(H.grad_style[GRADIENT_HAIR_KEY]), length(SSaccessories.hair_gradients_list))
L[DNA_HAIR_COLOR_GRADIENT_BLOCK] = sanitize_hexcolor(H.grad_color[GRADIENT_HAIR_KEY], include_crunch = FALSE)
L[DNA_FACIAL_HAIRSTYLE_GRADIENT_BLOCK] = construct_block(SSaccessories.facial_hair_gradients_list.Find(H.grad_style[GRADIENT_FACIAL_HAIR_KEY]), length(SSaccessories.facial_hair_gradients_list))
L[DNA_FACIAL_HAIR_COLOR_GRADIENT_BLOCK] = sanitize_hexcolor(H.grad_color[GRADIENT_FACIAL_HAIR_KEY], include_crunch = FALSE)
for(var/blocknum in 1 to DNA_UNI_IDENTITY_BLOCKS)
. += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters)
for(var/block_type in GLOB.dna_identity_blocks)
var/datum/dna_block/identity/block = GLOB.dna_identity_blocks[block_type]
. += block.unique_block(holder)
/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["lizard_markings"])
L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(SSaccessories.lizard_markings_list.Find(features["lizard_markings"]), length(SSaccessories.lizard_markings_list))
if(features["tail_cat"])
L[DNA_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_felinid.Find(features["tail_cat"]), length(SSaccessories.tails_list_felinid))
if(features["tail_lizard"])
L[DNA_LIZARD_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_lizard.Find(features["tail_lizard"]), length(SSaccessories.tails_list_lizard))
if(features["snout"])
L[DNA_SNOUT_BLOCK] = construct_block(SSaccessories.snouts_list.Find(features["snout"]), length(SSaccessories.snouts_list))
if(features["horns"])
L[DNA_HORNS_BLOCK] = construct_block(SSaccessories.horns_list.Find(features["horns"]), length(SSaccessories.horns_list))
if(features["frills"])
L[DNA_FRILLS_BLOCK] = construct_block(SSaccessories.frills_list.Find(features["frills"]), length(SSaccessories.frills_list))
if(features["spines"])
L[DNA_SPINES_BLOCK] = construct_block(SSaccessories.spines_list.Find(features["spines"]), length(SSaccessories.spines_list))
if(features["ears"])
L[DNA_EARS_BLOCK] = construct_block(SSaccessories.ears_list.Find(features["ears"]), length(SSaccessories.ears_list))
if(features["moth_wings"] != "Burnt Off")
L[DNA_MOTH_WINGS_BLOCK] = construct_block(SSaccessories.moth_wings_list.Find(features["moth_wings"]), length(SSaccessories.moth_wings_list))
if(features["moth_antennae"] != "Burnt Off")
L[DNA_MOTH_ANTENNAE_BLOCK] = construct_block(SSaccessories.moth_antennae_list.Find(features["moth_antennae"]), length(SSaccessories.moth_antennae_list))
if(features["moth_markings"])
L[DNA_MOTH_MARKINGS_BLOCK] = construct_block(SSaccessories.moth_markings_list.Find(features["moth_markings"]), length(SSaccessories.moth_markings_list))
if(features["caps"])
L[DNA_MUSHROOM_CAPS_BLOCK] = construct_block(SSaccessories.caps_list.Find(features["caps"]), length(SSaccessories.caps_list))
if(features["pod_hair"])
L[DNA_POD_HAIR_BLOCK] = construct_block(SSaccessories.pod_hair_list.Find(features["pod_hair"]), length(SSaccessories.pod_hair_list))
if(features["fish_tail"])
L[DNA_FISH_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_fish.Find(features["fish_tail"]), length(SSaccessories.tails_list_fish))
for(var/blocknum in 1 to DNA_FEATURE_BLOCKS)
. += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters)
for(var/block_type in GLOB.dna_feature_blocks)
var/datum/dna_block/feature/block = GLOB.dna_feature_blocks[block_type]
if(isnull(features[block.feature_key]))
. += random_string(block.block_length, GLOB.hex_characters)
continue
. += block.unique_block(holder)
/**
* Picks what mutations this DNA has innate and generates DNA blocks for them
@@ -341,96 +256,27 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
. += 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)
CRASH("UI block index is null")
/datum/dna/proc/update_ui_block(blocktype)
if(isnull(blocktype))
CRASH("UI block type 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)
set_uni_identity_block(blocknumber, sanitize_hexcolor(H.hair_color, include_crunch = FALSE))
if(DNA_FACIAL_HAIR_COLOR_BLOCK)
set_uni_identity_block(blocknumber, sanitize_hexcolor(H.facial_hair_color, include_crunch = FALSE))
if(DNA_SKIN_TONE_BLOCK)
set_uni_identity_block(blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len))
if(DNA_EYE_COLOR_LEFT_BLOCK)
set_uni_identity_block(blocknumber, sanitize_hexcolor(H.eye_color_left, include_crunch = FALSE))
if(DNA_EYE_COLOR_RIGHT_BLOCK)
set_uni_identity_block(blocknumber, sanitize_hexcolor(H.eye_color_right, include_crunch = FALSE))
if(DNA_GENDER_BLOCK)
switch(H.gender)
if(MALE)
set_uni_identity_block(blocknumber, construct_block(G_MALE, GENDERS))
if(FEMALE)
set_uni_identity_block(blocknumber, construct_block(G_FEMALE, GENDERS))
if(NEUTER)
set_uni_identity_block(blocknumber, construct_block(G_NEUTER, GENDERS))
else
set_uni_identity_block(blocknumber, construct_block(G_PLURAL, GENDERS))
if(DNA_FACIAL_HAIRSTYLE_BLOCK)
set_uni_identity_block(blocknumber, construct_block(SSaccessories.facial_hairstyles_list.Find(H.facial_hairstyle), length(SSaccessories.facial_hairstyles_list)))
if(DNA_HAIRSTYLE_BLOCK)
set_uni_identity_block(blocknumber, construct_block(SSaccessories.hairstyles_list.Find(H.hairstyle), length(SSaccessories.hairstyles_list)))
if(DNA_HAIRSTYLE_GRADIENT_BLOCK)
set_uni_identity_block(blocknumber, construct_block(SSaccessories.hair_gradients_list.Find(H.grad_style[GRADIENT_HAIR_KEY]), length(SSaccessories.hair_gradients_list)))
if(DNA_FACIAL_HAIRSTYLE_GRADIENT_BLOCK)
set_uni_identity_block(blocknumber, construct_block(SSaccessories.facial_hair_gradients_list.Find(H.grad_style[GRADIENT_FACIAL_HAIR_KEY]), length(SSaccessories.facial_hair_gradients_list)))
if(DNA_HAIR_COLOR_GRADIENT_BLOCK)
set_uni_identity_block(blocknumber, sanitize_hexcolor(H.grad_color[GRADIENT_HAIR_KEY], include_crunch = FALSE))
if(DNA_FACIAL_HAIR_COLOR_GRADIENT_BLOCK)
set_uni_identity_block(blocknumber, sanitize_hexcolor(H.grad_color[GRADIENT_FACIAL_HAIR_KEY], include_crunch = FALSE))
var/datum/dna_block/identity/block = GLOB.dna_identity_blocks[blocktype]
unique_identity = block.modified_hash(unique_identity, block.unique_block(holder))
/datum/dna/proc/update_uf_block(blocknumber)
if(!blocknumber)
CRASH("UF block index is null")
/datum/dna/proc/update_uf_block(blocktype)
if(!blocktype)
CRASH("UF block type 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(SSaccessories.lizard_markings_list.Find(features["lizard_markings"]), length(SSaccessories.lizard_markings_list)))
if(DNA_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_felinid.Find(features["tail_cat"]), length(SSaccessories.tails_list_felinid)))
if(DNA_LIZARD_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_lizard.Find(features["tail_lizard"]), length(SSaccessories.tails_list_lizard)))
if(DNA_SNOUT_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.snouts_list.Find(features["snout"]), length(SSaccessories.snouts_list)))
if(DNA_HORNS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.horns_list.Find(features["horns"]), length(SSaccessories.horns_list)))
if(DNA_FRILLS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.frills_list.Find(features["frills"]), length(SSaccessories.frills_list)))
if(DNA_SPINES_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.spines_list.Find(features["spines"]), length(SSaccessories.spines_list)))
if(DNA_EARS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.ears_list.Find(features["ears"]), length(SSaccessories.ears_list)))
if(DNA_MOTH_WINGS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.moth_wings_list.Find(features["moth_wings"]), length(SSaccessories.moth_wings_list)))
if(DNA_MOTH_ANTENNAE_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.moth_antennae_list.Find(features["moth_antennae"]), length(SSaccessories.moth_antennae_list)))
if(DNA_MOTH_MARKINGS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.moth_markings_list.Find(features["moth_markings"]), length(SSaccessories.moth_markings_list)))
if(DNA_MUSHROOM_CAPS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.caps_list.Find(features["caps"]), length(SSaccessories.caps_list)))
if(DNA_POD_HAIR_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.pod_hair_list.Find(features["pod_hair"]), length(SSaccessories.pod_hair_list)))
if(DNA_FISH_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_fish.Find(features["fish_tail"]), length(SSaccessories.tails_list_fish)))
var/datum/dna_block/feature/block = GLOB.dna_identity_blocks[blocktype]
unique_features = block.modified_hash(unique_features, block.unique_block(holder))
/**
* Checks if two DNAs are practically the same by comparing their most defining features
@@ -480,6 +326,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
/// Updates the UI, UE, and UF of the DNA according to the features, appearance, name, etc. of the DNA / holder.
/datum/dna/proc/update_dna_identity()
if(!holder.has_dna())
return
unique_identity = generate_unique_identity()
unique_enzymes = generate_unique_enzymes()
unique_features = generate_unique_features()
@@ -502,7 +350,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
for(var/feature in new_features)
features[feature] = new_features[feature]
features["mcolor"] = "#[random_color()]"
features[FEATURE_MUTANT_COLOR] = "#[random_color()]"
update_dna_identity()
@@ -636,88 +484,25 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
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)
//2025: Im unsure if dna is meant to be living, carbon, or human level.. there's contradicting stuff and bugfixes going back 8 years
//If youre reading this, and you know for sure, update this, or maybe remove the carbon part entirely
/mob/living/carbon/proc/updateappearance(icon_update = TRUE, mutcolor_update = FALSE, mutations_overlay_update = FALSE)
if(!has_dna())
return
//Always plural gender if agender
if(HAS_TRAIT(src, TRAIT_AGENDER))
gender = PLURAL
return
switch(deconstruct_block(get_uni_identity_block(dna.unique_identity, DNA_GENDER_BLOCK), GENDERS))
if(G_MALE)
gender = MALE
if(G_FEMALE)
gender = FEMALE
if(G_NEUTER)
gender = NEUTER
else
gender = PLURAL
/mob/living/carbon/human/updateappearance(icon_update = TRUE, mutcolor_update = FALSE, mutations_overlay_update = FALSE)
..()
var/structure = dna.unique_identity
skin_tone = GLOB.skin_tones[deconstruct_block(get_uni_identity_block(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)]
set_eye_color(sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_LEFT_BLOCK)), sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_RIGHT_BLOCK)))
set_haircolor(sanitize_hexcolor(get_uni_identity_block(structure, DNA_HAIR_COLOR_BLOCK)), update = FALSE)
set_facial_haircolor(sanitize_hexcolor(get_uni_identity_block(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)), update = FALSE)
set_hair_gradient_color(sanitize_hexcolor(get_uni_identity_block(structure, DNA_HAIR_COLOR_GRADIENT_BLOCK)), update = FALSE)
set_facial_hair_gradient_color(sanitize_hexcolor(get_uni_identity_block(structure, DNA_FACIAL_HAIR_COLOR_GRADIENT_BLOCK)), update = FALSE)
if(HAS_TRAIT(src, TRAIT_SHAVED))
set_facial_hairstyle("Shaved", update = FALSE)
else
var/style = SSaccessories.facial_hairstyles_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIRSTYLE_BLOCK), length(SSaccessories.facial_hairstyles_list))]
var/gradient_style = SSaccessories.facial_hair_gradients_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIRSTYLE_GRADIENT_BLOCK), length(SSaccessories.facial_hair_gradients_list))]
set_facial_hairstyle(style, update = FALSE)
set_facial_hair_gradient_style(gradient_style, update = FALSE)
if(HAS_TRAIT(src, TRAIT_BALD))
set_hairstyle("Bald", update = FALSE)
else
var/style = SSaccessories.hairstyles_list[deconstruct_block(get_uni_identity_block(structure, DNA_HAIRSTYLE_BLOCK), length(SSaccessories.hairstyles_list))]
var/gradient_style = SSaccessories.hair_gradients_list[deconstruct_block(get_uni_identity_block(structure, DNA_HAIRSTYLE_GRADIENT_BLOCK), length(SSaccessories.hair_gradients_list))]
set_hairstyle(style, update = FALSE)
set_hair_gradient_style(gradient_style, update = FALSE)
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["lizard_markings"])
dna.features["lizard_markings"] = SSaccessories.lizard_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_MARKINGS_BLOCK), length(SSaccessories.lizard_markings_list))]
if(dna.features["snout"])
dna.features["snout"] = SSaccessories.snouts_list[deconstruct_block(get_uni_feature_block(features, DNA_SNOUT_BLOCK), length(SSaccessories.snouts_list))]
if(dna.features["horns"])
dna.features["horns"] = SSaccessories.horns_list[deconstruct_block(get_uni_feature_block(features, DNA_HORNS_BLOCK), length(SSaccessories.horns_list))]
if(dna.features["frills"])
dna.features["frills"] = SSaccessories.frills_list[deconstruct_block(get_uni_feature_block(features, DNA_FRILLS_BLOCK), length(SSaccessories.frills_list))]
if(dna.features["spines"])
dna.features["spines"] = SSaccessories.spines_list[deconstruct_block(get_uni_feature_block(features, DNA_SPINES_BLOCK), length(SSaccessories.spines_list))]
if(dna.features["tail_cat"])
dna.features["tail_cat"] = SSaccessories.tails_list_felinid[deconstruct_block(get_uni_feature_block(features, DNA_TAIL_BLOCK), length(SSaccessories.tails_list_felinid))]
if(dna.features["tail_lizard"])
dna.features["tail_lizard"] = SSaccessories.tails_list_lizard[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_TAIL_BLOCK), length(SSaccessories.tails_list_lizard))]
if(dna.features["ears"])
dna.features["ears"] = SSaccessories.ears_list[deconstruct_block(get_uni_feature_block(features, DNA_EARS_BLOCK), length(SSaccessories.ears_list))]
if(dna.features["moth_wings"])
var/genetic_value = SSaccessories.moth_wings_list[deconstruct_block(get_uni_feature_block(features, DNA_MOTH_WINGS_BLOCK), length(SSaccessories.moth_wings_list))]
dna.features["original_moth_wings"] = genetic_value
dna.features["moth_wings"] = genetic_value
if(dna.features["moth_antennae"])
var/genetic_value = SSaccessories.moth_antennae_list[deconstruct_block(get_uni_feature_block(features, DNA_MOTH_ANTENNAE_BLOCK), length(SSaccessories.moth_antennae_list))]
dna.features["original_moth_antennae"] = genetic_value
dna.features["moth_antennae"] = genetic_value
if(dna.features["moth_markings"])
dna.features["moth_markings"] = SSaccessories.moth_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_MOTH_MARKINGS_BLOCK), length(SSaccessories.moth_markings_list))]
if(dna.features["caps"])
dna.features["caps"] = SSaccessories.caps_list[deconstruct_block(get_uni_feature_block(features, DNA_MUSHROOM_CAPS_BLOCK), length(SSaccessories.caps_list))]
if(dna.features["pod_hair"])
dna.features["pod_hair"] = SSaccessories.pod_hair_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_HAIR_BLOCK), length(SSaccessories.pod_hair_list))]
if(dna.features["fish_tail"])
dna.features["fish_tail"] = SSaccessories.tails_list_fish[deconstruct_block(get_uni_feature_block(features, DNA_FISH_TAIL_BLOCK), length(SSaccessories.tails_list_fish))]
. = ..()
for(var/block_type in GLOB.dna_identity_blocks)
var/datum/dna_block/identity/block_to_apply = GLOB.dna_identity_blocks[block_type]
block_to_apply.apply_to_mob(src, dna.unique_identity)
for(var/block_type in GLOB.dna_feature_blocks)
var/datum/dna_block/feature/block_to_apply = GLOB.dna_feature_blocks[block_type]
if(dna.features[block_to_apply.feature_key])
block_to_apply.apply_to_mob(src, dna.unique_features)
for(var/obj/item/organ/organ in organs)
organ.mutate_feature(features, src)
organ.mutate_feature(dna.unique_features, src)
if(icon_update)
update_body(is_creating = mutcolor_update)
@@ -839,15 +624,17 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
/mob/living/carbon/proc/random_mutate_unique_identity()
if(!has_dna())
CRASH("[src] does not have DNA")
var/num = rand(1, DNA_UNI_IDENTITY_BLOCKS)
dna.set_uni_feature_block(num, random_string(GET_UI_BLOCK_LEN(num), GLOB.hex_characters))
updateappearance(mutations_overlay_update=1)
var/mutblock_path = pick(GLOB.dna_identity_blocks)
var/datum/dna_block/identity/mutblock = GLOB.dna_identity_blocks[mutblock_path]
dna.unique_identity = mutblock.modified_hash(dna.unique_identity, random_string(mutblock.block_length, GLOB.hex_characters))
updateappearance(mutations_overlay_update = TRUE)
/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))
var/mutblock_path = pick(GLOB.dna_feature_blocks)
var/datum/dna_block/feature/mutblock = GLOB.dna_feature_blocks[mutblock_path]
dna.unique_features = mutblock.modified_hash(dna.unique_features, random_string(mutblock.block_length, GLOB.hex_characters))
updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE)
/mob/living/carbon/proc/clean_dna()
@@ -859,7 +646,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
clean_dna()
random_mutate(candidates, difficulty)
/proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, uf=FALSE, probability)
/proc/scramble_dna(mob/living/carbon/M, ui = FALSE, se = FALSE, uf = FALSE, probability = 100)
if(!M.has_dna())
CRASH("[M] does not have DNA")
if(HAS_TRAIT(M, TRAIT_NO_DNA_SCRAMBLE))
@@ -870,13 +657,15 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
M.dna.generate_dna_blocks()
M.domutcheck()
if(ui)
for(var/blocknum in 1 to DNA_UNI_IDENTITY_BLOCKS)
for(var/block_id in GLOB.dna_identity_blocks)
var/datum/dna_block/identity/block = GLOB.dna_identity_blocks[block_id]
if(prob(probability))
M.dna.set_uni_feature_block(blocknum, random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters))
M.dna.unique_identity = block.modified_hash(M.dna.unique_identity, random_string(block.block_length, GLOB.hex_characters))
if(uf)
for(var/blocknum in 1 to DNA_FEATURE_BLOCKS)
for(var/block_id in GLOB.dna_feature_blocks)
var/datum/dna_block/feature/block = GLOB.dna_feature_blocks[block_id]
if(prob(probability))
M.dna.set_uni_feature_block(blocknum, random_string(GET_UF_BLOCK_LEN(blocknum), GLOB.hex_characters))
M.dna.unique_identity = block.modified_hash(M.dna.unique_identity, random_string(block.block_length, GLOB.hex_characters))
if(ui || uf)
M.updateappearance(mutcolor_update=uf, mutations_overlay_update=1)
@@ -897,12 +686,6 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
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)

View File

@@ -0,0 +1,69 @@
// This DNA block system is by no means perfect, and individual (Especially feature) blocks still contain a lot of copypaste
// It might be worth abstracting these, and compacting all SSaccessories vars that use dna_blocks into a single keyed list
// That's out of scope for me refactoring DNA, but to be considered for a future atomic change
/// A singleton for handling block-unique functions called by the DNA datum.
///
/// You don't need to add a DNA block for every feature.
/// Only add a new one if you want this feature to be changed via genetics.
/datum/dna_block
/// The length of this block when converted to ascii
var/block_length = DNA_BLOCK_SIZE
/// Returns the unique block created from target. To be used for external calls.
///
/// Does extra checks to make sure target is valid before calling the internal
/// `create_unique_block`, don't override this.
/datum/dna_block/proc/unique_block(mob/living/carbon/human/target)
SHOULD_NOT_OVERRIDE(TRUE)
if(!ishuman(target))
CRASH("Non-human mobs shouldn't have DNA")
return create_unique_block(target)
/// Actually creates the unique block from the inputted target.
/// Not used outside of the type, see `unique_block` instead.
///
/// Children should always override this.
/datum/dna_block/proc/create_unique_block(mob/living/carbon/human/target)
PROTECTED_PROC(TRUE)
return null
/// The position of this block's string in its hash type
/datum/dna_block/proc/position_in_hash()
return null
/// Takes in the old hash and a string value to change this block to inside the hash.
///
/// Returns a new hash with block's value updated
/datum/dna_block/proc/modified_hash(old_hash, value)
var/block_pos = position_in_hash()
if(isnull(block_pos))
return old_hash
var/preceding_blocks = copytext(old_hash, 1, block_pos)
var/succeeding_blocks = copytext(old_hash, block_pos + block_length)
return (preceding_blocks + value + succeeding_blocks)
/// Gets the block string from the hash inserted
/datum/dna_block/proc/get_block(from_hash)
if(isnull(from_hash))
CRASH("Null hash provided for getting dna block string")
var/block_pos = position_in_hash()
return copytext(from_hash, block_pos, block_pos + block_length)
/// Applies the DNA effects/appearance that this block's string encodes
/datum/dna_block/proc/apply_to_mob(mob/living/carbon/human/target, dna_hash)
return
/// Blocks for unique identities (skin tones, hair style, and gender)
/datum/dna_block/identity
/datum/dna_block/identity/position_in_hash()
return GLOB.total_ui_len_by_block[type]
/// Blocks for unique features (mutant color, mutant bodyparts)
/datum/dna_block/feature
/// The feature key this block ties in to.
var/feature_key = null
/datum/dna_block/feature/position_in_hash()
return GLOB.total_uf_len_by_block[type]

View File

@@ -20,7 +20,7 @@
var/was_not_hetero = !human_holder.eye_color_heterochromatic
human_holder.eye_color_heterochromatic = TRUE
human_holder.eye_color_right = color
human_holder.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK)
human_holder.dna.update_ui_block(/datum/dna_block/identity/eye_colors)
var/obj/item/organ/eyes/eyes_of_the_holder = quirk_holder.get_organ_by_type(/obj/item/organ/eyes)
if(!eyes_of_the_holder)

View File

@@ -220,7 +220,7 @@
greyscale_colors = FISH_ORGAN_COLOR
bodypart_overlay = /datum/bodypart_overlay/mutant/tail/fish
dna_block = DNA_FISH_TAIL_BLOCK
dna_block = /datum/dna_block/feature/tail_fish
wag_flags = NONE
organ_traits = list(TRAIT_FLOPPING, TRAIT_SWIMMER)
restyle_flags = EXTERNAL_RESTYLE_FLESH
@@ -282,15 +282,15 @@
source.add_traits(list(TRAIT_OFF_BALANCE_TACKLER, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), type)
/datum/bodypart_overlay/mutant/tail/fish
feature_key = "fish_tail"
feature_key = FEATURE_TAIL_FISH
color_source = ORGAN_COLOR_OVERRIDE
/datum/bodypart_overlay/mutant/tail/fish/on_mob_insert(obj/item/organ/parent, mob/living/carbon/receiver)
//Initialize the related dna feature block if we don't have any so it doesn't error out.
//This isn't tied to any species, but I kinda want it to be mutable instead of having a fixed sprite accessory.
if(imprint_on_next_insertion && !receiver.dna.features["fish_tail"])
receiver.dna.features["fish_tail"] = pick(SSaccessories.tails_list_fish)
receiver.dna.update_uf_block(DNA_FISH_TAIL_BLOCK)
if(imprint_on_next_insertion && !receiver.dna.features[FEATURE_TAIL_FISH])
receiver.dna.features[FEATURE_TAIL_FISH] = pick(SSaccessories.tails_list_fish)
receiver.dna.update_uf_block(FEATURE_TAIL_FISH)
return ..()

View File

@@ -6,6 +6,6 @@
visual = TRUE
damage_multiplier = 2
dna_block = DNA_EARS_BLOCK
dna_block = /datum/dna_block/feature/ears
bodypart_overlay = /datum/bodypart_overlay/mutant/cat_ears
sprite_accessory_override = /datum/sprite_accessory/ears/fox

View File

@@ -199,15 +199,15 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
var/new_s_tone = tgui_input_list(race_changer, "Choose your skin tone", "Race change", GLOB.skin_tones)
if(new_s_tone)
race_changer.skin_tone = new_s_tone
race_changer.dna.update_ui_block(DNA_SKIN_TONE_BLOCK)
race_changer.dna.update_ui_block(/datum/dna_block/identity/skin_tone)
else if(HAS_TRAIT(race_changer, TRAIT_MUTANT_COLORS) && !HAS_TRAIT(race_changer, TRAIT_FIXED_MUTANT_COLORS))
var/new_mutantcolor = input(race_changer, "Choose your skin color:", "Race change", race_changer.dna.features["mcolor"]) as color|null
var/new_mutantcolor = input(race_changer, "Choose your skin color:", "Race change", race_changer.dna.features[FEATURE_MUTANT_COLOR]) as color|null
if(new_mutantcolor)
var/list/mutant_hsv = rgb2hsv(new_mutantcolor)
if(mutant_hsv[3] >= 50) // mutantcolors must be bright
race_changer.dna.features["mcolor"] = sanitize_hexcolor(new_mutantcolor)
race_changer.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK)
race_changer.dna.features[FEATURE_MUTANT_COLOR] = sanitize_hexcolor(new_mutantcolor)
race_changer.dna.update_uf_block(FEATURE_MUTANT_COLOR)
else
to_chat(race_changer, span_notice("Invalid color. Your color is not bright enough."))
return TRUE
@@ -241,7 +241,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
if(chosen_physique && chosen_physique != "Wizards Don't Need Gender")
sexy.physique = (chosen_physique == "Warlock Physique") ? MALE : FEMALE
sexy.dna.update_ui_block(DNA_GENDER_BLOCK)
sexy.dna.update_ui_block(/datum/dna_block/identity/gender)
sexy.update_body(is_creating = TRUE) // or else physique won't change properly
sexy.update_mutations_overlay() //(hulk male/female)
sexy.update_clothing(ITEM_SLOT_ICLOTHING) // update gender shaped clothing
@@ -251,8 +251,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
if(isnull(new_eye_color))
return TRUE
user.set_eye_color(sanitize_hexcolor(new_eye_color))
user.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK)
user.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK)
user.dna.update_ui_block(/datum/dna_block/identity/eye_colors)
user.update_body()
to_chat(user, span_notice("You gaze at your new eyes with your new eyes. Perfect!"))
@@ -383,12 +382,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
if(new_hair_color)
user.set_haircolor(sanitize_hexcolor(new_hair_color))
user.dna.update_ui_block(DNA_HAIR_COLOR_BLOCK)
user.dna.update_ui_block(/datum/dna_block/identity/hair_color)
if(user.physique == MALE)
var/new_face_color = input(user, "Choose your facial hair color", "Hair Color", user.facial_hair_color) as color|null
if(new_face_color)
user.set_facial_haircolor(sanitize_hexcolor(new_face_color))
user.dna.update_ui_block(DNA_FACIAL_HAIR_COLOR_BLOCK)
user.dna.update_ui_block(/datum/dna_block/identity/facial_color)
/obj/structure/mirror/magic/attack_hand(mob/living/carbon/human/user)
. = ..()

View File

@@ -578,7 +578,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
SEND_SOUND(H, sound(SSstation.announcer.event_sounds[ANNOUNCER_ANIMES]))
if(H.dna.species.id == SPECIES_HUMAN)
if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None")
if(H.dna.features[FEATURE_TAIL] == "None" || H.dna.features[FEATURE_EARS] == "None")
var/obj/item/organ/ears/cat/ears = new
var/obj/item/organ/tail/cat/tail = new
ears.Insert(H, movement_flags = DELETE_IF_REPLACED)

View File

@@ -133,12 +133,12 @@
var/mob/living/carbon/human/dummy/consistent/brother1 = new
var/mob/living/carbon/human/dummy/consistent/brother2 = new
brother1.dna.features["ethcolor"] = GLOB.color_list_ethereal["Faint Red"]
brother1.dna.features[FEATURE_ETHEREAL_COLOR] = GLOB.color_list_ethereal["Faint Red"]
brother1.set_species(/datum/species/ethereal)
brother2.dna.features["moth_antennae"] = "Plain"
brother2.dna.features["moth_markings"] = "None"
brother2.dna.features["moth_wings"] = "Plain"
brother2.dna.features[FEATURE_MOTH_ANTENNAE] = "Plain"
brother2.dna.features[FEATURE_MOTH_MARKINGS] = "None"
brother2.dna.features[FEATURE_MOTH_WINGS] = "Plain"
brother2.set_species(/datum/species/moth)
var/icon/brother1_icon = render_preview_outfit(/datum/outfit/job/quartermaster, brother1)

View File

@@ -33,4 +33,4 @@
return icon
/datum/preference/choiced/ethereal_color/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["ethcolor"] = GLOB.color_list_ethereal[value]
target.dna.features[FEATURE_ETHEREAL_COLOR] = GLOB.color_list_ethereal[value]

View File

@@ -9,7 +9,7 @@
return assoc_to_keys_features(SSaccessories.tails_list_felinid)
/datum/preference/choiced/tail_felinid/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["tail_cat"] = value
target.dna.features[FEATURE_TAIL] = value
/datum/preference/choiced/tail_felinid/create_default_value()
var/datum/sprite_accessory/tails/felinid/cat/tail = /datum/sprite_accessory/tails/felinid/cat
@@ -26,7 +26,7 @@
return assoc_to_keys_features(SSaccessories.ears_list)
/datum/preference/choiced/felinid_ears/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["ears"] = value
target.dna.features[FEATURE_EARS] = value
/datum/preference/choiced/felinid_ears/create_default_value()
return /datum/sprite_accessory/ears/cat::name

View File

@@ -55,7 +55,7 @@
return final_icon
/datum/preference/choiced/lizard_body_markings/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["lizard_markings"] = value
target.dna.features[FEATURE_LIZARD_MARKINGS] = value
/datum/preference/choiced/lizard_frills
savefile_key = "feature_lizard_frills"
@@ -72,7 +72,7 @@
return generate_lizard_side_shot(SSaccessories.frills_list[value], "frills")
/datum/preference/choiced/lizard_frills/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["frills"] = value
target.dna.features[FEATURE_FRILLS] = value
/datum/preference/choiced/lizard_horns
savefile_key = "feature_lizard_horns"
@@ -89,7 +89,7 @@
return generate_lizard_side_shot(SSaccessories.horns_list[value], "horns")
/datum/preference/choiced/lizard_horns/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["horns"] = value
target.dna.features[FEATURE_HORNS] = value
/datum/preference/choiced/lizard_legs
savefile_key = "feature_lizard_legs"
@@ -100,7 +100,7 @@
return list(NORMAL_LEGS, DIGITIGRADE_LEGS)
/datum/preference/choiced/lizard_legs/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["legs"] = value
target.dna.features[FEATURE_LEGS] = value
// Hack to update the dummy in the preference menu
// (Because digi legs are ONLY handled on species change)
if(!isdummy(target) || target.dna.species.digitigrade_customization == DIGITIGRADE_NEVER)
@@ -145,7 +145,7 @@
return generate_lizard_side_shot(SSaccessories.snouts_list[value], "snout", include_snout = FALSE)
/datum/preference/choiced/lizard_snout/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["snout"] = value
target.dna.features[FEATURE_SNOUT] = value
/datum/preference/choiced/lizard_spines
savefile_key = "feature_lizard_spines"
@@ -157,7 +157,7 @@
return assoc_to_keys_features(SSaccessories.spines_list)
/datum/preference/choiced/lizard_spines/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["spines"] = value
target.dna.features[FEATURE_SPINES] = value
/datum/preference/choiced/lizard_tail
savefile_key = "feature_lizard_tail"
@@ -169,7 +169,7 @@
return assoc_to_keys_features(SSaccessories.tails_list_lizard)
/datum/preference/choiced/lizard_tail/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["tail_lizard"] = value
target.dna.features[FEATURE_TAIL_LIZARD] = value
/datum/preference/choiced/lizard_tail/create_default_value()
return /datum/sprite_accessory/tails/lizard/smooth::name

View File

@@ -9,7 +9,7 @@
return assoc_to_keys_features(SSaccessories.tails_list_monkey)
/datum/preference/choiced/monkey_tail/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["tail_monkey"] = value
target.dna.features[FEATURE_TAIL_MONKEY] = value
/datum/preference/choiced/monkey_tail/create_default_value()
return /datum/sprite_accessory/tails/monkey/default::name

View File

@@ -27,7 +27,7 @@
return icon_with_antennae
/datum/preference/choiced/moth_antennae/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["moth_antennae"] = value
target.dna.features[FEATURE_MOTH_ANTENNAE] = value
/datum/preference/choiced/moth_markings
savefile_key = "feature_moth_markings"
@@ -77,7 +77,7 @@
return icon_with_markings
/datum/preference/choiced/moth_markings/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["moth_markings"] = value
target.dna.features[FEATURE_MOTH_MARKINGS] = value
/datum/preference/choiced/moth_wings
savefile_key = "feature_moth_wings"
@@ -95,4 +95,4 @@
return uni_icon(moth_wings.icon, "m_moth_wings_[moth_wings.icon_state]_BEHIND")
/datum/preference/choiced/moth_wings/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["moth_wings"] = value
target.dna.features[FEATURE_MOTH_WINGS] = value

View File

@@ -8,4 +8,4 @@
return assoc_to_keys_features(SSaccessories.caps_list)
/datum/preference/choiced/mushroom_cap/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["caps"] = value
target.dna.features[FEATURE_MUSH_CAP] = value

View File

@@ -16,7 +16,7 @@
return sanitize_hexcolor("[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]")
/datum/preference/color/mutant_color/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["mcolor"] = value
target.dna.features[FEATURE_MUTANT_COLOR] = value
/datum/preference/color/mutant_color/is_valid(value)
if (!..(value))

View File

@@ -28,4 +28,4 @@
return pick(assoc_to_keys_features(SSaccessories.pod_hair_list))
/datum/preference/choiced/pod_hair/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["pod_hair"] = value
target.dna.features[FEATURE_POD_HAIR] = value

View File

@@ -199,10 +199,10 @@
podman.gender = blood_gender
podman.faction |= factions
if(!features["mcolor"])
features["mcolor"] = "#59CE00"
if(!features["pod_hair"])
features["pod_hair"] = pick(SSaccessories.pod_hair_list)
if(!features[FEATURE_MUTANT_COLOR])
features[FEATURE_MUTANT_COLOR] = "#59CE00"
if(!features[FEATURE_POD_HAIR])
features[FEATURE_POD_HAIR] = pick(SSaccessories.pod_hair_list)
for(var/V in quirks)
new V(podman)

View File

@@ -171,17 +171,17 @@
if(1)
to_chat(user, span_danger("Your appearance morphs to that of a very small humanoid ash dragon! You get to look like a freak without the cool abilities."))
consumer.dna.features = list(
"mcolor" = "#A02720",
"tail_lizard" = "Dark Tiger",
"tail_human" = "None",
"snout" = "Sharp",
"horns" = "Curled",
"ears" = "None",
"wings" = "None",
"frills" = "None",
"spines" = "Long",
"lizard_markings" = "Dark Tiger Body",
"legs" = DIGITIGRADE_LEGS,
FEATURE_MUTANT_COLOR = "#A02720",
FEATURE_TAIL_LIZARD = "Dark Tiger",
FEATURE_TAIL = "None",
FEATURE_SNOUT = "Sharp",
FEATURE_HORNS = "Curled",
FEATURE_EARS = "None",
FEATURE_WINGS = "None",
FEATURE_FRILLS = "None",
FEATURE_SPINES = "Long",
FEATURE_LIZARD_MARKINGS = "Dark Tiger Body",
FEATURE_LEGS = DIGITIGRADE_LEGS,
)
consumer.set_eye_color("#FEE5A3")
consumer.set_species(/datum/species/lizard)

View File

@@ -350,9 +350,9 @@
// Ensures that lighter slimefolk look half-decent when wounded and bleeding
/datum/blood_type/slime/get_wound_color(mob/living/carbon/victim)
return victim.dna?.features?["mcolor"] || get_color()
return victim.dna?.features?[FEATURE_MUTANT_COLOR] || get_color()
/datum/blood_type/slime/get_damage_color(mob/living/carbon/victim)
return victim.dna?.features?["mcolor"] || get_color()
return victim.dna?.features?[FEATURE_MUTANT_COLOR] || get_color()
/// Podpeople blood
/datum/blood_type/water

View File

@@ -1996,7 +1996,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
//Note for future: Potentionally add a new C.dna.species() to build a template species for more accurate limb replacement
var/list/final_bodypart_overrides = new_species.bodypart_overrides.Copy()
if((new_species.digitigrade_customization == DIGITIGRADE_OPTIONAL && target.dna.features["legs"] == DIGITIGRADE_LEGS) || new_species.digitigrade_customization == DIGITIGRADE_FORCED)
if((new_species.digitigrade_customization == DIGITIGRADE_OPTIONAL && target.dna.features[FEATURE_LEGS] == DIGITIGRADE_LEGS) || new_species.digitigrade_customization == DIGITIGRADE_FORCED)
final_bodypart_overrides[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade
final_bodypart_overrides[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade
@@ -2048,7 +2048,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
ASSERT(!isnull(for_mob))
switch(hair_color_mode)
if(USE_MUTANT_COLOR)
return for_mob.dna.features["mcolor"]
return for_mob.dna.features[FEATURE_MUTANT_COLOR]
if(USE_FIXED_MUTANT_COLOR)
return fixed_mut_color
@@ -2065,7 +2065,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
continue
var/datum/bodypart_overlay/simple/body_marking/overlay = new markings_type()
overlay.set_appearance(accessory_name, hooman.dna.features["mcolor"])
overlay.set_appearance(accessory_name, hooman.dna.features[FEATURE_MUTANT_COLOR])
people_part.add_bodypart_overlay(overlay)
qdel(markings)

View File

@@ -105,23 +105,23 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
return consistent_entry
/proc/create_consistent_human_dna(mob/living/carbon/human/target)
target.dna.features["mcolor"] = COLOR_VIBRANT_LIME
target.dna.features["ethcolor"] = COLOR_WHITE
target.dna.features["lizard_markings"] = get_consistent_feature_entry(SSaccessories.lizard_markings_list)
target.dna.features["ears"] = get_consistent_feature_entry(SSaccessories.ears_list)
target.dna.features["frills"] = get_consistent_feature_entry(SSaccessories.frills_list)
target.dna.features["horns"] = get_consistent_feature_entry(SSaccessories.horns_list)
target.dna.features["moth_antennae"] = get_consistent_feature_entry(SSaccessories.moth_antennae_list)
target.dna.features["moth_markings"] = get_consistent_feature_entry(SSaccessories.moth_markings_list)
target.dna.features["moth_wings"] = get_consistent_feature_entry(SSaccessories.moth_wings_list)
target.dna.features["snout"] = get_consistent_feature_entry(SSaccessories.snouts_list)
target.dna.features["spines"] = get_consistent_feature_entry(SSaccessories.spines_list)
target.dna.features["tail_cat"] = get_consistent_feature_entry(SSaccessories.tails_list_felinid) // it's a lie
target.dna.features["tail_lizard"] = get_consistent_feature_entry(SSaccessories.tails_list_lizard)
target.dna.features["tail_monkey"] = get_consistent_feature_entry(SSaccessories.tails_list_monkey)
target.dna.features["fish_tail"] = get_consistent_feature_entry(SSaccessories.tails_list_fish)
target.dna.features["pod_hair"] = get_consistent_feature_entry(SSaccessories.pod_hair_list)
target.dna.features["caps"] = get_consistent_feature_entry(SSaccessories.caps_list)
target.dna.features[FEATURE_MUTANT_COLOR] = COLOR_VIBRANT_LIME
target.dna.features[FEATURE_ETHEREAL_COLOR] = COLOR_WHITE
target.dna.features[FEATURE_LIZARD_MARKINGS] = get_consistent_feature_entry(SSaccessories.lizard_markings_list)
target.dna.features[FEATURE_EARS] = get_consistent_feature_entry(SSaccessories.ears_list)
target.dna.features[FEATURE_FRILLS] = get_consistent_feature_entry(SSaccessories.frills_list)
target.dna.features[FEATURE_HORNS] = get_consistent_feature_entry(SSaccessories.horns_list)
target.dna.features[FEATURE_MOTH_ANTENNAE] = get_consistent_feature_entry(SSaccessories.moth_antennae_list)
target.dna.features[FEATURE_MOTH_MARKINGS] = get_consistent_feature_entry(SSaccessories.moth_markings_list)
target.dna.features[FEATURE_MOTH_WINGS] = get_consistent_feature_entry(SSaccessories.moth_wings_list)
target.dna.features[FEATURE_SNOUT] = get_consistent_feature_entry(SSaccessories.snouts_list)
target.dna.features[FEATURE_SPINES] = get_consistent_feature_entry(SSaccessories.spines_list)
target.dna.features[FEATURE_TAIL] = get_consistent_feature_entry(SSaccessories.tails_list_felinid) // it's a lie
target.dna.features[FEATURE_TAIL_LIZARD] = get_consistent_feature_entry(SSaccessories.tails_list_lizard)
target.dna.features[FEATURE_TAIL_MONKEY] = get_consistent_feature_entry(SSaccessories.tails_list_monkey)
target.dna.features[FEATURE_TAIL_FISH] = get_consistent_feature_entry(SSaccessories.tails_list_fish)
target.dna.features[FEATURE_POD_HAIR] = get_consistent_feature_entry(SSaccessories.pod_hair_list)
target.dna.features[FEATURE_MUSH_CAP] = get_consistent_feature_entry(SSaccessories.caps_list)
target.dna.initialize_dna(newblood_type = get_blood_type(BLOOD_TYPE_O_MINUS), create_mutation_blocks = FALSE, randomize_features = FALSE)
// UF and UI are nondeterministic, even though the features are the same some blocks will randomize slightly
// In practice this doesn't matter, but this is for the sake of 100%(ish) consistency

View File

@@ -55,7 +55,7 @@
. = ..()
if(!ishuman(new_ethereal))
return
default_color = new_ethereal.dna.features["ethcolor"]
default_color = new_ethereal.dna.features[FEATURE_ETHEREAL_COLOR]
RegisterSignal(new_ethereal, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag_act))
RegisterSignal(new_ethereal, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act))
RegisterSignal(new_ethereal, COMSIG_ATOM_SABOTEUR_ACT, PROC_REF(hit_by_saboteur))
@@ -84,7 +84,7 @@
/datum/species/ethereal/randomize_features()
var/list/features = ..()
features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
features[FEATURE_ETHEREAL_COLOR] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
return features
/datum/species/ethereal/proc/refresh_light_color(mob/living/carbon/human/ethereal)
@@ -95,7 +95,7 @@
var/healthpercent = max(ethereal.health, 0) / 100
if(!emageffect)
var/static/list/skin_color = rgb2num("#eda495")
var/list/colors = rgb2num(ethereal.dna.features["ethcolor"])
var/list/colors = rgb2num(ethereal.dna.features[FEATURE_ETHEREAL_COLOR])
var/list/built_color = list()
for(var/i in 1 to 3)
built_color += skin_color[i] + ((colors[i] - skin_color[i]) * healthpercent)
@@ -334,5 +334,5 @@
/datum/species/ethereal/lustrous/on_species_gain(mob/living/carbon/new_lustrous, datum/species/old_species, pref_load, regenerate_icons)
..()
default_color = new_lustrous.dna.features["ethcolor"]
new_lustrous.dna.features["ethcolor"] = GLOB.color_list_lustrous[pick(GLOB.color_list_lustrous)] //Picks one of 5 lustrous-specific colors.
default_color = new_lustrous.dna.features[FEATURE_ETHEREAL_COLOR]
new_lustrous.dna.features[FEATURE_ETHEREAL_COLOR] = GLOB.color_list_lustrous[pick(GLOB.color_list_lustrous)] //Picks one of 5 lustrous-specific colors.

View File

@@ -28,19 +28,19 @@
if(ishuman(carbon_being))
var/mob/living/carbon/human/target_human = carbon_being
if(!pref_load) //Hah! They got forcefully purrbation'd. Force default felinid parts on them if they have no mutant parts in those areas!
target_human.dna.features["tail_cat"] = "Cat"
if(target_human.dna.features["ears"] == "None")
target_human.dna.features["ears"] = "Cat"
if(target_human.dna.features["ears"] == "None")
target_human.dna.features[FEATURE_TAIL] = "Cat"
if(target_human.dna.features[FEATURE_EARS] == "None")
target_human.dna.features[FEATURE_EARS] = "Cat"
if(target_human.dna.features[FEATURE_EARS] == "None")
mutantears = /obj/item/organ/ears
else
var/obj/item/organ/ears/cat/ears = new(FALSE, target_human.dna.features["ears"])
var/obj/item/organ/ears/cat/ears = new(FALSE, target_human.dna.features[FEATURE_EARS])
ears.Insert(target_human, movement_flags = DELETE_IF_REPLACED)
return ..()
/datum/species/human/felinid/randomize_features(mob/living/carbon/human/human_mob)
var/list/features = ..()
features["ears"] = pick("None", "Cat")
features[FEATURE_EARS] = pick("None", "Cat")
return features
/datum/species/human/felinid/get_laugh_sound(mob/living/carbon/human/felinid)

View File

@@ -107,7 +107,7 @@
)
/datum/species/jelly/prepare_human_for_preview(mob/living/carbon/human/human)
human.dna.features["mcolor"] = COLOR_PINK
human.dna.features[FEATURE_MUTANT_COLOR] = COLOR_PINK
human.hairstyle = "Bob Hair 2"
human.hair_color = COLOR_PINK
human.update_body(is_creating = TRUE)
@@ -304,8 +304,8 @@
spare.underwear = "Nude"
H.dna.copy_dna(spare.dna, COPY_DNA_SE|COPY_DNA_SPECIES|COPY_DNA_MUTATIONS)
spare.dna.features["mcolor"] = "#[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]"
spare.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK)
spare.dna.features[FEATURE_MUTANT_COLOR] = "#[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]"
spare.dna.update_uf_block(FEATURE_MUTANT_COLOR)
spare.real_name = spare.dna.real_name
spare.name = spare.dna.real_name
spare.updateappearance(mutcolor_update=1)
@@ -371,7 +371,7 @@
continue
var/list/L = list()
L["htmlcolor"] = body.dna.features["mcolor"]
L["htmlcolor"] = body.dna.features[FEATURE_MUTANT_COLOR]
L["area"] = get_area_name(body, TRUE)
var/stat = "error"
switch(body.stat)
@@ -541,7 +541,7 @@
/datum/species/jelly/luminescent/proc/update_glow(mob/living/carbon/human/glowie, intensity)
if(intensity)
glow_intensity = intensity
glow.set_light_range_power_color(glow_intensity, glow_intensity, glowie.dna.features["mcolor"])
glow.set_light_range_power_color(glow_intensity, glow_intensity, glowie.dna.features[FEATURE_MUTANT_COLOR])
/datum/action/innate/integrate_extract
name = "Integrate Extract"

View File

@@ -51,7 +51,7 @@
/datum/species/lizard/randomize_features()
var/list/features = ..()
features["lizard_markings"] = pick(SSaccessories.lizard_markings_list)
features[FEATURE_LIZARD_MARKINGS] = pick(SSaccessories.lizard_markings_list)
return features
/datum/species/lizard/get_scream_sound(mob/living/carbon/human/lizard)
@@ -225,14 +225,14 @@ Lizard subspecies: SILVER SCALED
and their tongue allows them to turn into a statue, for some reason."
/datum/species/lizard/silverscale/on_species_gain(mob/living/carbon/human/new_silverscale, datum/species/old_species, pref_load, regenerate_icons)
old_mutcolor = new_silverscale.dna.features["mcolor"]
new_silverscale.dna.features["mcolor"] = "#eeeeee"
old_mutcolor = new_silverscale.dna.features[FEATURE_MUTANT_COLOR]
new_silverscale.dna.features[FEATURE_MUTANT_COLOR] = "#eeeeee"
new_silverscale.add_eye_color("#0000a0", EYE_COLOR_SPECIES_PRIORITY)
. = ..()
new_silverscale.add_filter("silver_glint", 2, list("type" = "outline", "color" = "#ffffff63", "size" = 2))
/datum/species/lizard/silverscale/on_species_loss(mob/living/carbon/human/was_silverscale, datum/species/new_species, pref_load)
was_silverscale.dna.features["mcolor"] = old_mutcolor
was_silverscale.dna.features[FEATURE_MUTANT_COLOR] = old_mutcolor
was_silverscale.remove_eye_color(EYE_COLOR_SPECIES_PRIORITY)
was_silverscale.remove_filter("silver_glint")
return ..()

View File

@@ -45,7 +45,7 @@
/datum/species/moth/randomize_features()
var/list/features = ..()
features["moth_markings"] = pick(SSaccessories.moth_markings_list)
features[FEATURE_MOTH_MARKINGS] = pick(SSaccessories.moth_markings_list)
return features
/datum/species/moth/get_scream_sound(mob/living/carbon/human/moth)

View File

@@ -61,7 +61,7 @@
preference = "feature_mushperson_cap"
dna_block = DNA_MUSHROOM_CAPS_BLOCK
dna_block = /datum/dna_block/feature/mush_cap
restyle_flags = EXTERNAL_RESTYLE_PLANT
bodypart_overlay = /datum/bodypart_overlay/mutant/mushroom_cap
@@ -71,7 +71,7 @@
/// Bodypart overlay for the mushroom cap organ
/datum/bodypart_overlay/mutant/mushroom_cap
layers = EXTERNAL_ADJACENT
feature_key = "caps"
feature_key = FEATURE_MUSH_CAP
dyable = TRUE
/datum/bodypart_overlay/mutant/mushroom_cap/get_global_feature_list()

View File

@@ -40,8 +40,8 @@
)
/datum/species/pod/prepare_human_for_preview(mob/living/carbon/human/human)
human.dna.features["mcolor"] = "#886600"
human.dna.features["pod_hair"] = "Rose"
human.dna.features[FEATURE_MUTANT_COLOR] = "#886600"
human.dna.features[FEATURE_POD_HAIR] = "Rose"
human.update_body(is_creating = TRUE)
/datum/species/pod/get_physical_attributes()

View File

@@ -28,7 +28,7 @@
)
/datum/species/snail/prepare_human_for_preview(mob/living/carbon/human/human)
human.dna.features["mcolor"] = COLOR_BEIGE
human.dna.features[FEATURE_MUTANT_COLOR] = COLOR_BEIGE
human.update_body(is_creating = TRUE)
/datum/species/snail/get_physical_attributes()

View File

@@ -31,14 +31,8 @@
/mob/living/carbon/proc/on_agender_trait_loss(datum/source)
SIGNAL_HANDLER
//updates our gender to be whatever our DNA wants it to be
switch(deconstruct_block(get_uni_identity_block(dna.unique_identity, DNA_GENDER_BLOCK), 3) || pick(G_MALE, G_FEMALE))
if(G_MALE)
gender = MALE
if(G_FEMALE)
gender = FEMALE
else
gender = PLURAL
var/datum/dna_block/identity/gender/to_update = GLOB.dna_identity_blocks[/datum/dna_block/identity/gender]
to_update.apply_to_mob(src, src.dna.unique_identity)
/**
* On gain of TRAIT_NOBREATH

View File

@@ -168,7 +168,7 @@
if(HAS_TRAIT(affected_human, TRAIT_USES_SKINTONES))
affected_human.skin_tone = "green"
else if(HAS_TRAIT(affected_human, TRAIT_MUTANT_COLORS) && !HAS_TRAIT(affected_human, TRAIT_FIXED_MUTANT_COLORS)) //Code stolen from spraytan overdose
affected_human.dna.features["mcolor"] = "#a8e61d"
affected_human.dna.features[FEATURE_MUTANT_COLOR] = "#a8e61d"
affected_human.update_body(is_creating = TRUE)
/datum/reagent/consumable/ethanol/kahlua

View File

@@ -589,7 +589,7 @@
exposed_human.skin_tone = "mixed3"
//take current alien color and darken it slightly
else if(HAS_TRAIT(exposed_human, TRAIT_MUTANT_COLORS) && !HAS_TRAIT(exposed_human, TRAIT_FIXED_MUTANT_COLORS))
var/list/existing_color = rgb2num(exposed_human.dna.features["mcolor"])
var/list/existing_color = rgb2num(exposed_human.dna.features[FEATURE_MUTANT_COLOR])
var/list/darkened_color = list()
// Reduces each part of the color by 16
for(var/channel in existing_color)
@@ -599,7 +599,7 @@
var/list/new_hsv = rgb2hsv(new_color)
// Can't get too dark now
if(new_hsv[3] >= 50)
exposed_human.dna.features["mcolor"] = new_color
exposed_human.dna.features[FEATURE_MUTANT_COLOR] = new_color
exposed_human.update_body(is_creating = TRUE)
if((methods & INGEST) && show_message)
@@ -623,7 +623,7 @@
if(HAS_TRAIT(affected_human, TRAIT_USES_SKINTONES))
affected_human.skin_tone = "orange"
else if(HAS_TRAIT(affected_human, TRAIT_MUTANT_COLORS) && !HAS_TRAIT(affected_human, TRAIT_FIXED_MUTANT_COLORS)) //Aliens with custom colors simply get turned orange
affected_human.dna.features["mcolor"] = "#ff8800"
affected_human.dna.features[FEATURE_MUTANT_COLOR] = "#ff8800"
affected_human.update_body(is_creating = TRUE)
if(SPT_PROB(3.5, seconds_per_tick))
if(affected_human.w_uniform)

View File

@@ -606,8 +606,8 @@
/obj/item/slime_extract/rainbow/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type)
switch(activation_type)
if(SLIME_ACTIVATE_MINOR)
user.dna.features["mcolor"] = "#[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]"
user.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK)
user.dna.features[FEATURE_MUTANT_COLOR] = "#[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]"
user.dna.update_uf_block(FEATURE_MUTANT_COLOR)
user.updateappearance(mutcolor_update=1)
species.update_glow(user)
to_chat(user, span_notice("You feel different..."))

View File

@@ -982,7 +982,7 @@
if(owner_species.fixed_mut_color)
species_color = owner_species.fixed_mut_color
else
species_color = human_owner.dna.features["mcolor"]
species_color = human_owner.dna.features[FEATURE_MUTANT_COLOR]
else
skin_tone = ""
species_color = ""

View File

@@ -10,7 +10,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/// The savefile_key of the preference this relates to. Used for the preferences UI.
var/preference
///With what DNA block do we mutate in mutate_feature() ? For genetics
var/dna_block
var/datum/dna_block/dna_block
///Set to EXTERNAL_BEHIND, EXTERNAL_FRONT or EXTERNAL_ADJACENT if you want to draw one of those layers as the object sprite. FALSE to use your own
///This will not work if it doesn't have a limb to generate its icon with
@@ -75,7 +75,8 @@ Unlike normal organs, we're actually inside a persons limbs at all times
var/list/feature_list = bodypart_overlay.get_global_feature_list()
bodypart_overlay.set_appearance_from_name(feature_list[deconstruct_block(get_uni_feature_block(features, dna_block), feature_list.len)])
var/datum/dna_block/feature/feature_block = GLOB.dna_feature_blocks[dna_block]
bodypart_overlay.set_appearance_from_name(feature_list[deconstruct_block(feature_block.get_block(features), feature_list.len)])
///If you need to change an external_organ for simple one-offs, use this. Pass the accessory type : /datum/accessory/something
/obj/item/organ/proc/simple_change_sprite(accessory_type)
@@ -109,7 +110,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
slot = ORGAN_SLOT_EXTERNAL_HORNS
preference = "feature_lizard_horns"
dna_block = DNA_HORNS_BLOCK
dna_block = /datum/dna_block/feature/horn
restyle_flags = EXTERNAL_RESTYLE_ENAMEL
bodypart_overlay = /datum/bodypart_overlay/mutant/horns
@@ -118,7 +119,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/datum/bodypart_overlay/mutant/horns
layers = EXTERNAL_ADJACENT
feature_key = "horns"
feature_key = FEATURE_HORNS
dyable = TRUE
/datum/bodypart_overlay/mutant/horns/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
@@ -142,7 +143,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
slot = ORGAN_SLOT_EXTERNAL_FRILLS
preference = "feature_lizard_frills"
dna_block = DNA_FRILLS_BLOCK
dna_block = /datum/dna_block/feature/frill
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/frills
@@ -151,7 +152,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/datum/bodypart_overlay/mutant/frills
layers = EXTERNAL_ADJACENT
feature_key = "frills"
feature_key = FEATURE_FRILLS
/datum/bodypart_overlay/mutant/frills/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
var/mob/living/carbon/human/human = bodypart_owner.owner
@@ -176,7 +177,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
preference = "feature_lizard_snout"
external_bodyshapes = BODYSHAPE_SNOUTED
dna_block = DNA_SNOUT_BLOCK
dna_block = /datum/dna_block/feature/snout
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/snout
@@ -185,7 +186,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/datum/bodypart_overlay/mutant/snout
layers = EXTERNAL_ADJACENT
feature_key = "snout"
feature_key = FEATURE_SNOUT
/datum/bodypart_overlay/mutant/snout/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
var/mob/living/carbon/human/human = bodypart_owner.owner
@@ -208,7 +209,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
slot = ORGAN_SLOT_EXTERNAL_ANTENNAE
preference = "feature_moth_antennae"
dna_block = DNA_MOTH_ANTENNAE_BLOCK
dna_block = /datum/dna_block/feature/moth_antenna
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/antennae
@@ -262,7 +263,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
///Moth antennae datum, with full burning functionality
/datum/bodypart_overlay/mutant/antennae
layers = EXTERNAL_FRONT | EXTERNAL_BEHIND
feature_key = "moth_antennae"
feature_key = FEATURE_MOTH_ANTENNAE
dyable = TRUE
///Accessory datum of the burn sprite
var/datum/sprite_accessory/burn_datum = /datum/sprite_accessory/moth_antennae/burnt_off
@@ -299,7 +300,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
preference = "feature_pod_hair"
use_mob_sprite_as_obj_sprite = TRUE
dna_block = DNA_POD_HAIR_BLOCK
dna_block = /datum/dna_block/feature/pod_hair
restyle_flags = EXTERNAL_RESTYLE_PLANT
bodypart_overlay = /datum/bodypart_overlay/mutant/pod_hair
@@ -309,7 +310,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
///Podperson bodypart overlay, with special coloring functionality to render the flowers in the inverse color
/datum/bodypart_overlay/mutant/pod_hair
layers = EXTERNAL_FRONT|EXTERNAL_ADJACENT
feature_key = "pod_hair"
feature_key = FEATURE_POD_HAIR
dyable = TRUE
///This layer will be colored differently than the rest of the organ. So we can get differently colored flowers or something

View File

@@ -9,7 +9,7 @@
preference = "feature_lizard_spines"
dna_block = DNA_SPINES_BLOCK
dna_block = /datum/dna_block/feature/spine
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/spines
@@ -31,7 +31,7 @@
///Bodypart overlay for spines
/datum/bodypart_overlay/mutant/spines
layers = EXTERNAL_ADJACENT|EXTERNAL_BEHIND
feature_key = "spines"
feature_key = FEATURE_SPINES
dyable = TRUE
/datum/bodypart_overlay/mutant/spines/get_global_feature_list()

View File

@@ -7,7 +7,7 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_EXTERNAL_TAIL
dna_block = DNA_TAIL_BLOCK
dna_block = /datum/dna_block/feature/tail
restyle_flags = EXTERNAL_RESTYLE_FLESH
// defaults to cat, but the parent type shouldn't be created regardless
@@ -65,7 +65,7 @@
tail_spines_overlay = new
tail_spines_overlay.tail_spine_key = tail_spine_key
var/feature_name = bodypart.owner.dna.features["spines"] //tail spines don't live in DNA, but share feature names with regular spines
var/feature_name = bodypart.owner.dna.features[FEATURE_SPINES] //tail spines don't live in DNA, but share feature names with regular spines
tail_spines_overlay.set_appearance_from_name(feature_name)
bodypart.add_bodypart_overlay(tail_spines_overlay)
@@ -169,7 +169,7 @@
///Cat tail bodypart overlay
/datum/bodypart_overlay/mutant/tail/cat
feature_key = "tail_cat"
feature_key = FEATURE_TAIL
color_source = ORGAN_COLOR_HAIR
/datum/bodypart_overlay/mutant/tail/cat/get_global_feature_list()
@@ -185,7 +185,7 @@
///Monkey tail bodypart overlay
/datum/bodypart_overlay/mutant/tail/monkey
color_source = NONE
feature_key = "tail_monkey"
feature_key = FEATURE_TAIL_MONKEY
/datum/bodypart_overlay/mutant/tail/monkey/get_global_feature_list()
return SSaccessories.tails_list_monkey
@@ -228,7 +228,7 @@
///Alien tail bodypart overlay
/datum/bodypart_overlay/mutant/tail/xeno
color_source = NONE
feature_key = "tail_xeno"
feature_key = FEATURE_TAIL_XENO
imprint_on_next_insertion = FALSE
/// We don't want to bother writing this in DNA, just use this appearance
var/default_appearance = "Xeno"
@@ -254,11 +254,11 @@
bodypart_overlay = /datum/bodypart_overlay/mutant/tail/lizard
wag_flags = WAG_ABLE
dna_block = DNA_LIZARD_TAIL_BLOCK
dna_block = /datum/dna_block/feature/tail_lizard
///Lizard tail bodypart overlay datum
/datum/bodypart_overlay/mutant/tail/lizard
feature_key = "tail_lizard"
feature_key = FEATURE_TAIL_LIZARD
/datum/bodypart_overlay/mutant/tail/lizard/get_global_feature_list()
return SSaccessories.tails_list_lizard
@@ -270,7 +270,7 @@
///Bodypart overlay for tail spines. Handled by the tail - has no actual organ associated.
/datum/bodypart_overlay/mutant/tail_spines
layers = EXTERNAL_ADJACENT|EXTERNAL_BEHIND
feature_key = "tailspines"
feature_key = FEATURE_TAILSPINES
///Spines wag when the tail does
var/wagging = FALSE
/// Key for tail spine states, depends on the shape of the tail. Defined in the tail sprite datum.

View File

@@ -7,7 +7,7 @@
preference = "feature_moth_wings"
dna_block = DNA_MOTH_WINGS_BLOCK
dna_block = /datum/dna_block/feature/moth_wing
bodypart_overlay = /datum/bodypart_overlay/mutant/wings/moth
restyle_flags = EXTERNAL_RESTYLE_FLESH
@@ -101,7 +101,7 @@
///Moth wing bodypart overlay, including burn functionality!
/datum/bodypart_overlay/mutant/wings/moth
feature_key = "moth_wings"
feature_key = FEATURE_MOTH_WINGS
layers = EXTERNAL_BEHIND | EXTERNAL_FRONT
///Accessory datum of the burn sprite
var/datum/sprite_accessory/burn_datum = /datum/sprite_accessory/moth_wings/burnt_off

View File

@@ -22,7 +22,7 @@
///Bodypart overlay of default wings. Does not have any wing functionality
/datum/bodypart_overlay/mutant/wings
layers = ALL_EXTERNAL_OVERLAYS
feature_key = "wings"
feature_key = FEATURE_WINGS
/datum/bodypart_overlay/mutant/wings/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
var/mob/living/carbon/human/human = bodypart_owner.owner

View File

@@ -160,7 +160,7 @@
preference = "feature_human_ears"
restyle_flags = EXTERNAL_RESTYLE_FLESH
dna_block = DNA_EARS_BLOCK
dna_block = /datum/dna_block/feature/ears
bodypart_overlay = /datum/bodypart_overlay/mutant/cat_ears
@@ -168,7 +168,7 @@
/datum/bodypart_overlay/mutant/cat_ears
layers = EXTERNAL_FRONT | EXTERNAL_BEHIND
color_source = ORGAN_COLOR_HAIR
feature_key = "ears"
feature_key = FEATURE_EARS
dyable = TRUE
/// Layer upon which we add the inner ears overlay

View File

@@ -103,7 +103,7 @@
var/mob/living/carbon/human/human = carbon
if(human.dna?.species)
//fixed_mut_color is also ethereal color (for some reason)
carbon.flash_lighting_fx(5, 7, human.dna.species.fixed_mut_color ? human.dna.species.fixed_mut_color : human.dna.features["mcolor"])
carbon.flash_lighting_fx(5, 7, human.dna.species.fixed_mut_color ? human.dna.species.fixed_mut_color : human.dna.features[FEATURE_MUTANT_COLOR])
playsound(carbon, 'sound/effects/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
carbon.cut_overlay(overcharge)

View File

@@ -26,7 +26,7 @@
TEST_ASSERT_EQUAL(victim.real_name, ling_name, "Victim real name did not change on being transformation stung.")
TEST_ASSERT_EQUAL(victim.name, ling_name, "Victim name did not change on being transformation stung.")
TEST_ASSERT_EQUAL(victim.dna.species.type, ling.dna.species.type, "Victim species did not change on being transformation stung.")
TEST_ASSERT_EQUAL(victim.dna.features["mcolor"], ling.dna.features["mcolor"], "Victim mcolor did not change on being transformation stung.")
TEST_ASSERT_EQUAL(victim.dna.features[FEATURE_MUTANT_COLOR], ling.dna.features[FEATURE_MUTANT_COLOR], "Victim mcolor did not change on being transformation stung.")
// Check they actually look the same
add_to_screenshot(ling, victim)
@@ -37,7 +37,7 @@
TEST_ASSERT_EQUAL(victim.name, base_victim_name, "Victim name did not change back after transformation sting expired.")
TEST_ASSERT_EQUAL(victim.real_name, base_victim_name, "Victim real name did not change back after transformation sting expired.")
TEST_ASSERT_NOTEQUAL(victim.dna.species.type, ling.dna.species.type, "Victim species did not change back after transformation sting expired.")
TEST_ASSERT_NOTEQUAL(victim.dna.features["mcolor"], ling.dna.features["mcolor"], "Victim mcolor did not reset after transformation sting expired.")
TEST_ASSERT_NOTEQUAL(victim.dna.features[FEATURE_MUTANT_COLOR], ling.dna.features[FEATURE_MUTANT_COLOR], "Victim mcolor did not reset after transformation sting expired.")
// Check they actually look different again
add_to_screenshot(ling, victim, both_species = TRUE)
@@ -74,17 +74,16 @@
// Because we use two consistent humans, we need to change some of the features to know they're actually updating to new values.
// The more DNA features and random things we change, the more likely we are to catch something not updating correctly.
// Yeah guess who/what this is, I dare you.
ling.dna.features["mcolor"] = "#886600"
ling.dna.features["tail_lizard"] = "Smooth"
ling.dna.features["snout"] = "Sharp + Light"
ling.dna.features["horns"] = "Curled"
ling.dna.features["frills"] = "Short"
ling.dna.features["spines"] = "Long + Membrane"
ling.dna.features["lizard_markings"] = "Light Belly"
ling.dna.features["legs"] = DIGITIGRADE_LEGS
ling.dna.features[FEATURE_MUTANT_COLOR] = "#886600"
ling.dna.features[FEATURE_TAIL_LIZARD] = "Smooth"
ling.dna.features[FEATURE_SNOUT] = "Sharp + Light"
ling.dna.features[FEATURE_HORNS] = "Curled"
ling.dna.features[FEATURE_FRILLS] = "Short"
ling.dna.features[FEATURE_SPINES] = "Long + Membrane"
ling.dna.features[FEATURE_LIZARD_MARKINGS] = "Light Belly"
ling.dna.features[FEATURE_LEGS] = DIGITIGRADE_LEGS
ling.set_eye_color(COLOR_WHITE)
ling.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK)
ling.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK)
ling.dna.update_ui_block(/datum/dna_block/identity/eye_colors)
ling.set_species(/datum/species/lizard)
ling.real_name = ling_name

View File

@@ -6,12 +6,12 @@
// Test lizards as their own thing so we can get more coverage on their features
var/mob/living/carbon/human/lizard = allocate(/mob/living/carbon/human/dummy/consistent)
lizard.dna.features["mcolor"] = "#099"
lizard.dna.features["tail_lizard"] = "Light Tiger"
lizard.dna.features["snout"] = "Sharp + Light"
lizard.dna.features["horns"] = "Simple"
lizard.dna.features["frills"] = "Aquatic"
lizard.dna.features["legs"] = "Normal Legs"
lizard.dna.features[FEATURE_MUTANT_COLOR] = "#099"
lizard.dna.features[FEATURE_TAIL_LIZARD] = "Light Tiger"
lizard.dna.features[FEATURE_SNOUT] = "Sharp + Light"
lizard.dna.features[FEATURE_HORNS] = "Simple"
lizard.dna.features[FEATURE_FRILLS] = "Aquatic"
lizard.dna.features[FEATURE_LEGS] = "Normal Legs"
lizard.set_species(/datum/species/lizard)
lizard.equipOutfit(/datum/outfit/job/engineer)
test_screenshot("[/datum/species/lizard]", get_flat_icon_for_all_directions(lizard))
@@ -24,9 +24,9 @@
// let me have this
var/mob/living/carbon/human/moth = allocate(/mob/living/carbon/human/dummy/consistent)
moth.dna.features["moth_antennae"] = "Firewatch"
moth.dna.features["moth_markings"] = "None"
moth.dna.features["moth_wings"] = "Firewatch"
moth.dna.features[FEATURE_MOTH_ANTENNAE] = "Firewatch"
moth.dna.features[FEATURE_MOTH_MARKINGS] = "None"
moth.dna.features[FEATURE_MOTH_WINGS] = "Firewatch"
moth.set_species(/datum/species/moth)
moth.equipOutfit(/datum/outfit/job/cmo, visuals_only = TRUE)
test_screenshot("[/datum/species/moth]", get_flat_icon_for_all_directions(moth))
@@ -35,7 +35,7 @@
// More in depth test for slimes since they have a lot going on
for (var/datum/species/slime_type as anything in typesof(/datum/species/jelly))
var/mob/living/carbon/human/slime = allocate(/mob/living/carbon/human/dummy/consistent)
slime.dna.features["mcolor"] = COLOR_PINK
slime.dna.features[FEATURE_MUTANT_COLOR] = COLOR_PINK
slime.hairstyle = "Bob Hair 2"
slime.hair_color = COLOR_RED // Should be forced to pink
slime.set_species(slime_type)

View File

@@ -7,7 +7,7 @@
var/mob/living/carbon/human/morphing_human = allocate(/mob/living/carbon/human/dummy/consistent)
morphing_human.equipOutfit(/datum/outfit/job/assistant/consistent)
morphing_human.dna.features["legs"] = DIGITIGRADE_LEGS //you WILL have digitigrade legs
morphing_human.dna.features[FEATURE_LEGS] = DIGITIGRADE_LEGS //you WILL have digitigrade legs
var/obj/item/human_shoes = morphing_human.get_item_by_slot(ITEM_SLOT_FEET)
human_shoes.supports_variations_flags = NONE //do not fit lizards at all costs.

View File

@@ -812,7 +812,6 @@
#include "code\datums\dash_weapon.dm"
#include "code\datums\datum.dm"
#include "code\datums\datumvars.dm"
#include "code\datums\dna.dm"
#include "code\datums\dog_fashion.dm"
#include "code\datums\drift_handler.dm"
#include "code\datums\ductnet.dm"
@@ -1452,6 +1451,10 @@
#include "code\datums\diseases\floor_diseases\carpellosis.dm"
#include "code\datums\diseases\floor_diseases\gastritium.dm"
#include "code\datums\diseases\floor_diseases\nebula_nausea.dm"
#include "code\datums\dna\dna.dm"
#include "code\datums\dna\dna_block.dm"
#include "code\datums\dna\blocks\dna_features_block.dm"
#include "code\datums\dna\blocks\dna_identity_block.dm"
#include "code\datums\elements\_element.dm"
#include "code\datums\elements\ai_control_examine.dm"
#include "code\datums\elements\ai_flee_while_injured.dm"