diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 906a3b4de87c..36da4792dbaf 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -60,6 +60,7 @@ #define UI_CHANGED "ui changed" #define UE_CHANGED "ue changed" +#define UF_CHANGED "uf changed" #define CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY 255 #define CHAMELEON_MUTATION_MINIMUM_TRANSPARENCY 30 @@ -81,6 +82,8 @@ //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_UNI_IDENTITY_BLOCKS 7 #define DNA_HAIR_COLOR_BLOCK 1 #define DNA_FACIAL_HAIR_COLOR_BLOCK 2 @@ -90,6 +93,30 @@ #define DNA_FACIAL_HAIR_STYLE_BLOCK 6 #define DNA_HAIR_STYLE_BLOCK 7 +/// This number needs to equal the total number of DNA blocks +#define DNA_FEATURE_BLOCKS 19 + +#define DNA_MUTANT_COLOR_BLOCK 1 +#define DNA_ETHEREAL_COLOR_BLOCK 2 +#define DNA_LIZARD_MARKINGS_BLOCK 3 +#define DNA_LIZARD_TAIL_BLOCK 4 +#define DNA_SNOUT_BLOCK 5 +#define DNA_HORNS_BLOCK 6 +#define DNA_FRILLS_BLOCK 7 +#define DNA_SPINES_BLOCK 8 +#define DNA_HUMAN_TAIL_BLOCK 9 +#define DNA_EARS_BLOCK 10 +#define DNA_MOTH_WINGS_BLOCK 11 +#define DNA_MUSHROOM_CAPS_BLOCK 12 +#define DNA_POD_HAIR_BLOCK 13 +//Yog specific DNA Blocks +#define DNA_POD_FLOWER_BLOCK 14 +#define DNA_POLY_TAIL_BLOCK 15 +#define DNA_POLY_TEETH_BLOCK 16 +#define DNA_POLY_DOME_BLOCK 17 +#define DNA_POLY_DORSAL_BLOCK 18 +#define DNA_ETHEREAL_MARK_BLOCK 19 + #define DNA_SEQUENCE_LENGTH 4 #define DNA_MUTATION_BLOCKS 8 #define DNA_UNIQUE_ENZYMES_LEN 32 diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 91ff62653b06..ef64e821a051 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -1,7 +1,16 @@ // This is eventually for wjohn to add more color standardization stuff like I keep asking him >:( -#define COLOR_INPUT_DISABLED "#F0F0F0" -#define COLOR_INPUT_ENABLED "#D3B5B5" +//different types of atom colorations +/// Only used by rare effects like greentext coloring mobs and when admins varedit color +#define ADMIN_COLOUR_PRIORITY 1 +/// e.g. purple effect of the revenant on a mob, black effect when mob electrocuted +#define TEMPORARY_COLOUR_PRIORITY 2 +/// Color splashed onto an atom (e.g. paint on turf) +#define WASHABLE_COLOUR_PRIORITY 3 +/// Color inherent to the atom (e.g. blob color) +#define FIXED_COLOUR_PRIORITY 4 +///how many colour priority levels there are. +#define COLOUR_PRIORITY_AMOUNT 4 #define COLOR_DARKMODE_BACKGROUND "#202020" #define COLOR_DARKMODE_DARKBACKGROUND "#171717" @@ -122,31 +131,37 @@ #define COLOR_PURPLE_GRAY "#AE8CA8" //Color defines used by the assembly detailer. -#define COLOR_ASSEMBLY_BLACK "#545454" -#define COLOR_ASSEMBLY_BGRAY "#9497AB" -#define COLOR_ASSEMBLY_WHITE "#E2E2E2" -#define COLOR_ASSEMBLY_RED "#CC4242" -#define COLOR_ASSEMBLY_ORANGE "#E39751" -#define COLOR_ASSEMBLY_BEIGE "#AF9366" -#define COLOR_ASSEMBLY_BROWN "#97670E" -#define COLOR_ASSEMBLY_GOLD "#AA9100" -#define COLOR_ASSEMBLY_YELLOW "#CECA2B" -#define COLOR_ASSEMBLY_GURKHA "#999875" -#define COLOR_ASSEMBLY_LGREEN "#789876" -#define COLOR_ASSEMBLY_GREEN "#44843C" -#define COLOR_ASSEMBLY_LBLUE "#5D99BE" -#define COLOR_ASSEMBLY_BLUE "#38559E" -#define COLOR_ASSEMBLY_PURPLE "#6F6192" +#define COLOR_ASSEMBLY_BLACK "#545454" +#define COLOR_ASSEMBLY_BGRAY "#9497AB" +#define COLOR_ASSEMBLY_WHITE "#E2E2E2" +#define COLOR_ASSEMBLY_RED "#CC4242" +#define COLOR_ASSEMBLY_ORANGE "#E39751" +#define COLOR_ASSEMBLY_BEIGE "#AF9366" +#define COLOR_ASSEMBLY_BROWN "#97670E" +#define COLOR_ASSEMBLY_GOLD "#AA9100" +#define COLOR_ASSEMBLY_YELLOW "#CECA2B" +#define COLOR_ASSEMBLY_GURKHA "#999875" +#define COLOR_ASSEMBLY_LGREEN "#789876" +#define COLOR_ASSEMBLY_GREEN "#44843C" +#define COLOR_ASSEMBLY_LBLUE "#5D99BE" +#define COLOR_ASSEMBLY_BLUE "#38559E" +#define COLOR_ASSEMBLY_PURPLE "#6F6192" -/// The default color for admin say, used as a fallback when the preference is not enabled -#define DEFAULT_ASAY_COLOR "#996600" +///Colors for grayscale tools +#define COLOR_TOOL_BLUE "#1861d5" +#define COLOR_TOOL_RED "#951710" +#define COLOR_TOOL_PINK "#d5188d" +#define COLOR_TOOL_BROWN "#a05212" +#define COLOR_TOOL_GREEN "#0e7f1b" +#define COLOR_TOOL_CYAN "#18a2d5" +#define COLOR_TOOL_YELLOW "#d58c18" -/* - * Antag Specific Colors -*/ - -#define COLOR_CHANGELING_CHEMICALS "#DD66DD" -#define COLOR_DARKSPAWN_PSI "#7264FF" +///Colors for xenobiology vatgrowing +#define COLOR_SAMPLE_YELLOW "#c0b823" +#define COLOR_SAMPLE_PURPLE "#342941" +#define COLOR_SAMPLE_GREEN "#98b944" +#define COLOR_SAMPLE_BROWN "#91542d" +#define COLOR_SAMPLE_GRAY "#5e5856" ///Main colors for UI themes #define COLOR_THEME_MIDNIGHT "#6086A0" @@ -157,8 +172,15 @@ #define COLOR_THEME_GLASS "#75A4C4" #define COLOR_THEME_CLOCKWORK "#CFBA47" #define COLOR_THEME_TRASENKNOX "#3ce375" + +///yog UI colors #define COLOR_THEME_DETECTIVE "#c7b08b" +///Colors for eigenstates +#define COLOR_PERIWINKLEE "#9999FF" + +/// Starlight! +#define COLOR_STARLIGHT "#8589fa" /** * Some defines to generalise colours used in lighting. * @@ -168,68 +190,145 @@ /// Warm but extremely diluted red. rgb(250, 130, 130) #define LIGHT_COLOR_RED "#FA8282" /// Bright but quickly dissipating neon green. rgb(100, 200, 100) -#define LIGHT_COLOR_GREEN "#64C864" +#define LIGHT_COLOR_GREEN "#64C864" +/// Electric green. rgb(0, 255, 0) +#define LIGHT_COLOR_ELECTRIC_GREEN "#00FF00" /// Cold, diluted blue. rgb(100, 150, 250) -#define LIGHT_COLOR_BLUE "#6496FA" +#define LIGHT_COLOR_BLUE "#6496FA" /// Light blueish green. rgb(125, 225, 175) -#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" +#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" /// Diluted cyan. rgb(125, 225, 225) -#define LIGHT_COLOR_CYAN "#7DE1E1" +#define LIGHT_COLOR_CYAN "#7DE1E1" +/// Electric cyan rgb(0, 255, 255) +#define LIGHT_COLOR_ELECTRIC_CYAN "#00FFFF" /// More-saturated cyan. rgb(64, 206, 255) #define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" /// Saturated blue. rgb(51, 117, 248) -#define LIGHT_COLOR_DARK_BLUE "#6496FA" +#define LIGHT_COLOR_DARK_BLUE "#6496FA" /// Diluted, mid-warmth pink. rgb(225, 125, 225) -#define LIGHT_COLOR_PINK "#E17DE1" +#define LIGHT_COLOR_PINK "#E17DE1" /// Dimmed yellow, leaning kaki. rgb(225, 225, 125) -#define LIGHT_COLOR_YELLOW "#E1E17D" +#define LIGHT_COLOR_YELLOW "#E1E17D" /// Clear brown, mostly dim. rgb(150, 100, 50) -#define LIGHT_COLOR_BROWN "#966432" +#define LIGHT_COLOR_BROWN "#966432" /// Mostly pure orange. rgb(250, 150, 50) -#define LIGHT_COLOR_ORANGE "#FA9632" +#define LIGHT_COLOR_ORANGE "#FA9632" /// Light Purple. rgb(149, 44, 244) -#define LIGHT_COLOR_PURPLE "#952CF4" +#define LIGHT_COLOR_PURPLE "#952CF4" /// Less-saturated light purple. rgb(155, 81, 255) -#define LIGHT_COLOR_LAVENDER "#9B51FF" - -///slightly desaturated bright yellow. -#define LIGHT_COLOR_HOLY_MAGIC "#FFF743" +#define LIGHT_COLOR_LAVENDER "#9B51FF" +/// slightly desaturated bright yellow. +#define LIGHT_COLOR_HOLY_MAGIC "#FFF743" /// deep crimson -#define LIGHT_COLOR_BLOOD_MAGIC "#D00000" +#define LIGHT_COLOR_BLOOD_MAGIC "#D00000" +/// yog clockies color #define LIGHT_COLOR_CLOCKWORK "#BE8700" -//These ones aren't a direct colour like the ones above, because nothing would fit +/* These ones aren't a direct colour like the ones above, because nothing would fit */ /// Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25) -#define LIGHT_COLOR_FIRE "#FAA019" +#define LIGHT_COLOR_FIRE "#FAA019" /// Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24) -#define LIGHT_COLOR_LAVA "#C48A18" +#define LIGHT_COLOR_LAVA "#C48A18" /// Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) -#define LIGHT_COLOR_FLARE "#FA644B" +#define LIGHT_COLOR_FLARE "#FA644B" /// Weird color, between yellow and green, very slimy. rgb(175, 200, 75) #define LIGHT_COLOR_SLIME_LAMP "#AFC84B" /// Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) -#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" +#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" /// Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250) -#define LIGHT_COLOR_HALOGEN "#F0FAFA" - -#define CABLE_COLOR_BLUE "blue" - #define CABLE_HEX_COLOR_BLUE COLOR_STRONG_BLUE -#define CABLE_COLOR_BROWN "brown" - #define CABLE_HEX_COLOR_BROWN COLOR_ORANGE_BROWN -#define CABLE_COLOR_CYAN "cyan" - #define CABLE_HEX_COLOR_CYAN COLOR_CYAN -#define CABLE_COLOR_GREEN "green" - #define CABLE_HEX_COLOR_GREEN COLOR_DARK_LIME -#define CABLE_COLOR_ORANGE "orange" - #define CABLE_HEX_COLOR_ORANGE COLOR_MOSTLY_PURE_ORANGE -#define CABLE_COLOR_PINK "pink" - #define CABLE_HEX_COLOR_PINK COLOR_LIGHT_PINK -#define CABLE_COLOR_RED "red" - #define CABLE_HEX_COLOR_RED COLOR_RED -#define CABLE_COLOR_WHITE "white" - #define CABLE_HEX_COLOR_WHITE COLOR_WHITE -#define CABLE_COLOR_YELLOW "yellow" - #define CABLE_HEX_COLOR_YELLOW COLOR_YELLOW +#define LIGHT_COLOR_HALOGEN "#F0FAFA" + +//The GAGS greyscale_colors for each department's computer/machine circuits +#define CIRCUIT_COLOR_GENERIC "#1A7A13" +#define CIRCUIT_COLOR_COMMAND "#1B4594" +#define CIRCUIT_COLOR_SECURITY "#9A151E" +#define CIRCUIT_COLOR_SCIENCE "#BC4A9B" +#define CIRCUIT_COLOR_SERVICE "#92DCBA" +#define CIRCUIT_COLOR_MEDICAL "#00CCFF" +#define CIRCUIT_COLOR_ENGINEERING "#F8D700" +#define CIRCUIT_COLOR_SUPPLY "#C47749" + +/// Colors for pride week +#define COLOR_PRIDE_RED "#FF6666" +#define COLOR_PRIDE_ORANGE "#FC9F3C" +#define COLOR_PRIDE_YELLOW "#EAFF51" +#define COLOR_PRIDE_GREEN "#41FC66" +#define COLOR_PRIDE_BLUE "#42FFF2" +#define COLOR_PRIDE_PURPLE "#5D5DFC" + +/// The default color for admin say, used as a fallback when the preference is not enabled +#define DEFAULT_ASAY_COLOR COLOR_MOSTLY_PURE_RED + +#define DEFAULT_HEX_COLOR_LEN 6 + +// Color filters +/// Icon filter that creates ambient occlusion +#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, color="#04080FAA") +/// Icon filter that creates gaussian blur +#define GAUSSIAN_BLUR(filter_size) filter(type="blur", size=filter_size) + +/* + * Antag Specific Colors +*/ + +#define COLOR_CHANGELING_CHEMICALS "#DD66DD" +#define COLOR_DARKSPAWN_PSI "#7264FF" + +#define CABLE_COLOR_BLUE "blue" + #define CABLE_HEX_COLOR_BLUE COLOR_STRONG_BLUE +#define CABLE_COLOR_BROWN "brown" + #define CABLE_HEX_COLOR_BROWN COLOR_ORANGE_BROWN +#define CABLE_COLOR_CYAN "cyan" + #define CABLE_HEX_COLOR_CYAN COLOR_CYAN +#define CABLE_COLOR_GREEN "green" + #define CABLE_HEX_COLOR_GREEN COLOR_DARK_LIME +#define CABLE_COLOR_ORANGE "orange" + #define CABLE_HEX_COLOR_ORANGE COLOR_MOSTLY_PURE_ORANGE +#define CABLE_COLOR_PINK "pink" + #define CABLE_HEX_COLOR_PINK COLOR_LIGHT_PINK +#define CABLE_COLOR_RED "red" + #define CABLE_HEX_COLOR_RED COLOR_RED +#define CABLE_COLOR_WHITE "white" + #define CABLE_HEX_COLOR_WHITE COLOR_WHITE +#define CABLE_COLOR_YELLOW "yellow" + #define CABLE_HEX_COLOR_YELLOW COLOR_YELLOW + +#define COLOR_CARP_PURPLE "#aba2ff" +#define COLOR_CARP_PINK "#da77a8" +#define COLOR_CARP_GREEN "#70ff25" +#define COLOR_CARP_GRAPE "#df0afb" +#define COLOR_CARP_SWAMP "#e5e75a" +#define COLOR_CARP_TURQUOISE "#04e1ed" +#define COLOR_CARP_BROWN "#ca805a" +#define COLOR_CARP_TEAL "#20e28e" +#define COLOR_CARP_LIGHT_BLUE "#4d88cc" +#define COLOR_CARP_RUSTY "#dd5f34" +#define COLOR_CARP_RED "#fd6767" +#define COLOR_CARP_YELLOW "#f3ca4a" +#define COLOR_CARP_BLUE "#09bae1" +#define COLOR_CARP_PALE_GREEN "#7ef099" +#define COLOR_CARP_SILVER "#fdfbf3" +#define COLOR_CARP_DARK_BLUE "#3a384d" + +#define COLOR_GNOME_RED_ONE "#f10b0b" +#define COLOR_GNOME_RED_TWO "#bc5347" +#define COLOR_GNOME_RED_THREE "#b40f1a" +#define COLOR_GNOME_BLUE_ONE "#2e8ff7" +#define COLOR_GNOME_BLUE_TWO "#312bd6" +#define COLOR_GNOME_BLUE_THREE "#4e409a" +#define COLOR_GNOME_GREEN_ONE "#28da1c" +#define COLOR_GNOME_GREEN_TWO "#50a954" +#define COLOR_GNOME_YELLOW "#f6da3c" +#define COLOR_GNOME_ORANGE "#d56f2f" +#define COLOR_GNOME_BROWN_ONE "#874e2a" +#define COLOR_GNOME_BROWN_TWO "#543d2e" +#define COLOR_GNOME_PURPLE "#ac1dd7" +#define COLOR_GNOME_WHITE "#e8e8e8" +#define COLOR_GNOME_GREY "#a9a9a9" +#define COLOR_GNOME_BLACK "#303030" + +#define SOFA_BROWN "#a75400" +#define SOFA_MAROON "#830000" GLOBAL_LIST_INIT(cable_colors, list( CABLE_COLOR_BLUE = CABLE_HEX_COLOR_BLUE, @@ -242,3 +341,8 @@ GLOBAL_LIST_INIT(cable_colors, list( CABLE_COLOR_YELLOW = CABLE_HEX_COLOR_YELLOW, CABLE_COLOR_BROWN = CABLE_HEX_COLOR_BROWN )) + +// Yog Colors + +#define COLOR_INPUT_DISABLED "#F0F0F0" +#define COLOR_INPUT_ENABLED "#D3B5B5" diff --git a/code/__DEFINES/cult.dm b/code/__DEFINES/cult.dm index 871c6c5d00e2..b3b7a131be0a 100644 --- a/code/__DEFINES/cult.dm +++ b/code/__DEFINES/cult.dm @@ -26,7 +26,7 @@ #define DEFAULT_TOOLTIP "6:-29,5:-2" //misc #define SOULS_TO_REVIVE 3 -#define BLOODCULT_EYE "f00" +#define BLOODCULT_EYE "#FF0000" //soulstone & construct themes #define THEME_CULT "cult" #define THEME_WIZARD "wizard" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index c1a99d61b24b..9629ab27ed0b 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -307,13 +307,6 @@ GLOBAL_LIST_INIT(donor_pdas, list(PDA_COLOR_NORMAL, PDA_COLOR_TRANSPARENT, PDA_C #define LUMA_G 0.715 #define LUMA_B 0.072 -//different types of atom colorations -#define ADMIN_COLOUR_PRIORITY 1 //only used by rare effects like greentext coloring mobs and when admins varedit color -#define TEMPORARY_COLOUR_PRIORITY 2 //e.g. purple effect of the revenant on a mob, black effect when mob electrocuted -#define WASHABLE_COLOUR_PRIORITY 3 //color splashed onto an atom (e.g. paint on turf) -#define FIXED_COLOUR_PRIORITY 4 //color inherent to the atom (e.g. blob color) -#define COLOUR_PRIORITY_AMOUNT 4 //how many priority levels there are. - //Endgame Results #define NUKE_NEAR_MISS 1 #define NUKE_MISS_STATION 2 @@ -442,10 +435,6 @@ GLOBAL_LIST_INIT(donor_pdas, list(PDA_COLOR_NORMAL, PDA_COLOR_TRANSPARENT, PDA_C // Used by PDA and cartridge code to reduce repetitiveness of spritesheets #define PDAIMG(what) {""} -//Filters -#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, color="#04080FAA") -#define GAUSSIAN_BLUR(filter_size) filter(type="blur", size=filter_size) - #define STANDARD_GRAVITY 1 //Anything above this is high gravity, anything below no grav #define GRAVITY_DAMAGE_TRESHOLD 3 //Starting with this value gravity will start to damage mobs diff --git a/code/__HELPERS/dna.dm b/code/__HELPERS/dna.dm index 5ec66923ea54..0ec3e7ee8d49 100644 --- a/code/__HELPERS/dna.dm +++ b/code/__HELPERS/dna.dm @@ -10,4 +10,9 @@ #define GET_MUTATION_STABILIZER(A) ((A.stabilizer_coeff < 0) ? 1 : A.stabilizer_coeff) #define GET_MUTATION_SYNCHRONIZER(A) ((A.synchronizer_coeff < 0) ? 1 : A.synchronizer_coeff) #define GET_MUTATION_POWER(A) ((A.power_coeff < 0) ? 1 : A.power_coeff) -#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff) \ No newline at end of file +#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff) + +///Getter macro used to get the length of a identity block +#define GET_UI_BLOCK_LEN(blocknum) (GLOB.identity_block_lengths["[blocknum]"] || DNA_BLOCK_SIZE) +///Ditto, but for a feature. +#define GET_UF_BLOCK_LEN(blocknum) (GLOB.features_block_lengths["[blocknum]"] || DNA_BLOCK_SIZE) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 8e48ce3c35b9..ee4100adfe31 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -710,207 +710,185 @@ world ((hi3 >= 65 ? hi3-55 : hi3-48)<<4) | (lo3 >= 65 ? lo3-55 : lo3-48), ((hi4 >= 65 ? hi4-55 : hi4-48)<<4) | (lo4 >= 65 ? lo4-55 : lo4-48)) -// Creates a single icon from a given /atom or /image. Only the first argument is required. -/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) - //Define... defines. - var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") - - #define BLANK icon(flat_template) - #define SET_SELF(SETVAR) do { \ - var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ - if(A.alpha<255) { \ - SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ - } \ - if(A.color) { \ - if(islist(A.color)){ \ - SELF_ICON.MapColors(arglist(A.color))} \ - else{ \ - SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ - } \ - ##SETVAR=SELF_ICON;\ - } while (0) - #define INDEX_X_LOW 1 - #define INDEX_X_HIGH 2 - #define INDEX_Y_LOW 3 - #define INDEX_Y_HIGH 4 - - #define flatX1 flat_size[INDEX_X_LOW] - #define flatX2 flat_size[INDEX_X_HIGH] - #define flatY1 flat_size[INDEX_Y_LOW] - #define flatY2 flat_size[INDEX_Y_HIGH] - #define addX1 add_size[INDEX_X_LOW] - #define addX2 add_size[INDEX_X_HIGH] - #define addY1 add_size[INDEX_Y_LOW] - #define addY2 add_size[INDEX_Y_HIGH] - - if(!A || A.alpha <= 0) - return BLANK - - var/noIcon = FALSE +/// Create a single [/icon] from a given [/atom] or [/image]. +/// +/// Very low-performance. Should usually only be used for HTML, where BYOND's +/// appearance system (overlays/underlays, etc.) is not available. +/// +/// Only the first argument is required. +/proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + // Loop through the underlays, then overlays, sorting them into the layers list + #define PROCESS_OVERLAYS_OR_UNDERLAYS(flat, process, base_layer) \ + for (var/i in 1 to process.len) { \ + var/image/current = process[i]; \ + if (!current) { \ + continue; \ + } \ + if (current.plane != FLOAT_PLANE && current.plane != appearance.plane) { \ + continue; \ + } \ + var/current_layer = current.layer; \ + if (current_layer < 0) { \ + if (current_layer <= -1000) { \ + return flat; \ + } \ + current_layer = base_layer + appearance.layer + current_layer / 1000; \ + } \ + for (var/index_to_compare_to in 1 to layers.len) { \ + var/compare_to = layers[index_to_compare_to]; \ + if (current_layer < layers[compare_to]) { \ + layers.Insert(index_to_compare_to, current); \ + break; \ + } \ + } \ + layers[current] = current_layer; \ + } + + var/static/icon/flat_template = icon('icons/blanks/32x32.dmi', "nothing") + + if(!appearance || appearance.alpha <= 0) + return icon(flat_template) + if(start) if(!defdir) - defdir = A.dir + defdir = appearance.dir if(!deficon) - deficon = A.icon + deficon = appearance.icon if(!defstate) - defstate = A.icon_state + defstate = appearance.icon_state if(!defblend) - defblend = A.blend_mode + defblend = appearance.blend_mode + + var/curicon = appearance.icon || deficon + var/curstate = appearance.icon_state || defstate + var/curdir = (!appearance.dir || appearance.dir == SOUTH) ? defdir : appearance.dir - var/curicon = A.icon || deficon - var/curstate = A.icon_state || defstate + var/render_icon = curicon - if(!((noIcon = (!curicon)))) + if (render_icon) var/curstates = icon_states(curicon) if(!(curstate in curstates)) - if("" in curstates) + if ("" in curstates) curstate = "" else - noIcon = TRUE // Do not render this object. + render_icon = FALSE - var/curdir - var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have - - //These should use the parent's direction (most likely) - if(!A.dir || A.dir == SOUTH) - curdir = defdir - else - curdir = A.dir + var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have //Try to remove/optimize this section ASAP, CPU hog. //Determines if there's directionals. - if(!noIcon && curdir != SOUTH) - var/exist = FALSE - var/static/list/checkdirs = list(NORTH, EAST, WEST) - for(var/i in checkdirs) //Not using GLOB for a reason. - if(length(icon_states(icon(curicon, curstate, i)))) - exist = TRUE - break - if(!exist) + if(render_icon && curdir != SOUTH) + if ( + !length(icon_states(icon(curicon, curstate, NORTH))) \ + && !length(icon_states(icon(curicon, curstate, EAST))) \ + && !length(icon_states(icon(curicon, curstate, WEST))) \ + ) base_icon_dir = SOUTH - // if(!base_icon_dir) base_icon_dir = curdir - ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. + var/curblend = appearance.blend_mode || defblend - var/curblend = A.blend_mode || defblend - - if(A.overlays.len || A.underlays.len) - var/icon/flat = BLANK + if(appearance.overlays.len || appearance.underlays.len) + var/icon/flat = icon(flat_template) // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed var/list/layers = list() var/image/copy // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) - copy.color = A.color - copy.alpha = A.alpha + if(render_icon) + copy = image(icon=curicon, icon_state=curstate, layer=appearance.layer, dir=base_icon_dir) + copy.color = appearance.color + copy.alpha = appearance.alpha copy.blend_mode = curblend - layers[copy] = A.layer - - // Loop through the underlays, then overlays, sorting them into the layers list - for(var/process_set in 0 to 1) - var/list/process = process_set? A.overlays : A.underlays - for(var/i in 1 to process.len) - var/image/current = process[i] - if(!current) - continue - if(current.plane != FLOAT_PLANE && current.plane != A.plane) - continue - var/current_layer = current.layer - if(current_layer < 0) - if(current_layer <= -1000) - return flat - current_layer = process_set + A.layer + current_layer / 1000 - - for(var/p in 1 to layers.len) - var/image/cmp = layers[p] - if(current_layer < layers[cmp]) - layers.Insert(p, current) - break - layers[current] = current_layer - - //sortTim(layers, /proc/cmp_image_layer_asc) + layers[copy] = appearance.layer + + PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.underlays, 0) + PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.overlays, 1) var/icon/add // Icon of overlay being added - // Current dimensions of flattened icon - var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) - // Dimensions of overlay being added - var/list/add_size[4] + var/flatX1 = 1 + var/flatX2 = flat.Width() + var/flatY1 = 1 + var/flatY2 = flat.Height() - for(var/V in layers) - var/image/I = V - if(I.alpha == 0) + var/addX1 = 0 + var/addX2 = 0 + var/addY1 = 0 + var/addY2 = 0 + + for(var/image/layer_image as anything in layers) + if(layer_image.alpha == 0) continue - if(I == copy) // 'I' is an /image based on the object being flattened. + if(layer_image == copy) // 'layer_image' is an /image based on the object being flattened. curblend = BLEND_OVERLAY - add = icon(I.icon, I.icon_state, base_icon_dir) + add = icon(layer_image.icon, layer_image.icon_state, base_icon_dir) else // 'I' is an appearance object. - add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) + add = getFlatIcon(image(layer_image), curdir, curicon, curstate, curblend, FALSE, no_anim) if(!add) continue + // Find the new dimensions of the flat icon to fit the added overlay - add_size = list( - min(flatX1, I.pixel_x+1), - max(flatX2, I.pixel_x+add.Width()), - min(flatY1, I.pixel_y+1), - max(flatY2, I.pixel_y+add.Height()) + addX1 = min(flatX1, layer_image.pixel_x + 1) + addX2 = max(flatX2, layer_image.pixel_x + add.Width()) + addY1 = min(flatY1, layer_image.pixel_y + 1) + addY2 = max(flatY2, layer_image.pixel_y + add.Height()) + + if ( + addX1 != flatX1 \ + && addX2 != flatX2 \ + && addY1 != flatY1 \ + && addY2 != flatY2 \ ) - - if(flat_size ~! add_size) // Resize the flattened icon so the new icon fits flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 ) - flat_size = add_size.Copy() + + flatX1 = addX1 + flatX2 = addY1 + flatY1 = addX2 + flatY2 = addY2 // Blend the overlay into the flattened icon - flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) + flat.Blend(add, blendMode2iconMode(curblend), layer_image.pixel_x + 2 - flatX1, layer_image.pixel_y + 2 - flatY1) - if(A.color) - if(islist(A.color)) - flat.MapColors(arglist(A.color)) + if(appearance.color) + if(islist(appearance.color)) + flat.MapColors(arglist(appearance.color)) else - flat.Blend(A.color, ICON_MULTIPLY) + flat.Blend(appearance.color, ICON_MULTIPLY) - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + if(appearance.alpha < 255) + flat.Blend(rgb(255, 255, 255, appearance.alpha), ICON_MULTIPLY) if(no_anim) //Clean up repeated frames var/icon/cleaned = new /icon() cleaned.Insert(flat, "", SOUTH, 1, 0) - . = cleaned + return cleaned else - . = icon(flat, "", SOUTH) - else //There's no overlays. - if(!noIcon) - SET_SELF(.) - - //Clear defines - #undef flatX1 - #undef flatX2 - #undef flatY1 - #undef flatY2 - #undef addX1 - #undef addX2 - #undef addY1 - #undef addY2 - - #undef INDEX_X_LOW - #undef INDEX_X_HIGH - #undef INDEX_Y_LOW - #undef INDEX_Y_HIGH - - #undef BLANK - #undef SET_SELF + return icon(flat, "", SOUTH) + else if (render_icon) // There's no overlays. + var/icon/final_icon = icon(icon(curicon, curstate, base_icon_dir), "", SOUTH, no_anim ? TRUE : null) + + if (appearance.alpha < 255) + final_icon.Blend(rgb(255,255,255, appearance.alpha), ICON_MULTIPLY) + + if (appearance.color) + if (islist(appearance.color)) + final_icon.MapColors(arglist(appearance.color)) + else + final_icon.Blend(appearance.color, ICON_MULTIPLY) + + return final_icon + + #undef PROCESS_OVERLAYS_OR_UNDERLAYS /proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 62d6f348e67a..6fbf5760e179 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -4,21 +4,21 @@ /proc/random_eye_color() switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino")) if("brown") - return "630" + return "#663300" if("hazel") - return "542" + return "#554422" if("grey") - return pick("666","777","888","999","aaa","bbb","ccc") + return pick("#666666","#777777","#888888","#999999","#aaaaaa","#bbbbbb","#cccccc") if("blue") - return "36c" + return "#3366cc" if("green") - return "060" + return "#006600" if("amber") - return "fc0" + return "#ffcc00" if("albino") - return pick("c","d","e","f") + pick("0","1","2","3","4","5","6","7","8","9") + pick("0","1","2","3","4","5","6","7","8","9") + return "#" + pick("cc","dd","ee","ff") + pick("00","11","22","33","44","55","66","77","88","99") + pick("00","11","22","33","44","55","66","77","88","99") else - return "000" + return "#000000" /proc/random_underwear(gender) if(!GLOB.underwear_list.len) @@ -93,9 +93,9 @@ //For now we will always return none for tail_human and ears. this shit was unreadable if you do somethign like this make it at least readable return(list( - "mcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "mcolor" = "#[pick("7F","FF")][pick("7F","FF")][pick("7F","FF")]", "gradientstyle" = random_hair_gradient_style(10), - "gradientcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "gradientcolor" = "#[pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")]", "ethcolor" = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)], "pretcolor" = GLOB.color_list_preternis[pick(GLOB.color_list_preternis)], "tail_lizard" = pick(GLOB.tails_list_lizard), @@ -110,7 +110,7 @@ "legs" = "Normal Legs", "caps" = pick(GLOB.caps_list), "moth_wings" = pick(GLOB.moth_wings_list), - "tail_polysmorph" = "Polys", + "tail_polysmorph" = pick(GLOB.tails_list_polysmorph), "teeth" = pick(GLOB.teeth_list), "dome" = pick(GLOB.dome_list), "dorsal_tubes" = pick(GLOB.dorsal_tubes_list), diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index e3403250baa5..6b76a5c20bfa 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -29,7 +29,7 @@ return value if(default) return default - if(List && List.len) + if(List?.len) return pick(List) @@ -51,40 +51,64 @@ return default return default -/proc/sanitize_hexcolor(color, desired_format=3, include_crunch=0, default) +/** + * Sanitize_Hexcolor takes in a color in hexcode as a string, be it shorthand hex as 3 characters, or full-sized 6 digit hex, with or without a leading # + * you can pass it a full hexcode with leading #, such as "#FFFFFF", and with the default arguments you will get exactly that color back, because it accounts for + * leading # signs and ignores them, then later in the function will either readd one, or won't depending on what you want. + * + * Full hexcolors will just be validated, shorthand hex of 3 characters can be cleanly converted up to full hex with a leading # no problem. + * + * With default arguments: + * * "FFF" -> "#FFFFFF" + * * "#FFFFFF" -> "#FFFFFF" + * + * converting down to short, with or without the # is doable by setting the desired format to the length you want and specifying the crunch to true for adding a # or false to not + */ +/proc/sanitize_hexcolor(color, desired_format = DEFAULT_HEX_COLOR_LEN, include_crunch = TRUE, default) var/crunch = include_crunch ? "#" : "" if(!istext(color)) color = "" + //start checks for a leading "#", and if there is it skips past it before doing the color logic. + //this means you can pass something like "#FFFFFF" into sanitize_hexcolor without arguments, and it will automatically cut the leading #, and readd it at the end + //there is no risk of accidentally creating a malformed color like ##FFFFFF var/start = 1 + (text2ascii(color, 1) == 35) var/len = length(color) var/char = "" - // RRGGBB -> RGB but awful - var/convert_to_shorthand = desired_format == 3 && length_char(color) > 3 + // Used for conversion between RGBA hex formats. + var/format_input_ratio = "[desired_format]:[length_char(color)-(start-1)]" . = "" var/i = start while(i <= len) char = color[i] + i += length(char) switch(text2ascii(char)) - if(48 to 57) //numbers 0 to 9 + if(48 to 57) //numbers 0 to 9 + . += char + if(97 to 102) //letters a to f . += char - if(97 to 102) //letters a to f + if(65 to 70) //letters A to F + char = lowertext(char) . += char - if(65 to 70) //letters A to F - . += lowertext(char) else break - i += length(char) - if(convert_to_shorthand && i <= len) //skip next one - i += length(color[i]) - - if(length_char(.) != desired_format) - if(default) - return default - return crunch + repeat_string(desired_format, "0") + switch(format_input_ratio) + //if you're trying to convert up from short hex (3 characters) to a full hex 6, that's what these switch statements are doing, adding and removing to meet the desired format + if("3:8", "4:8", "3:6", "4:6") //skip next one. RRGGBB(AA) -> RGB(A) + i += length(color[i]) + if("6:4", "6:3", "8:4", "8:3") //add current char again. RGB(A) -> RRGGBB(AA) + . += char - return crunch + . + if(length_char(.) == desired_format) + return crunch + . + switch(format_input_ratio) //add or remove alpha channel depending on desired format. + if("3:8", "3:4", "6:4") + return crunch + copytext(., 1, desired_format+1) + if("4:6", "4:3", "8:3") + return crunch + . + ((desired_format == 4) ? "f" : "ff") + else //not a supported hex color format. + return default ? default : crunch + repeat_string(desired_format, "0") /proc/sanitize_color(color) if(length(color) != length_char(color)) diff --git a/code/_globalvars/_regexes.dm b/code/_globalvars/_regexes.dm deleted file mode 100644 index 59f468dcf022..000000000000 --- a/code/_globalvars/_regexes.dm +++ /dev/null @@ -1,3 +0,0 @@ -//These are a bunch of regex datums for use /((any|every|no|some|head|foot)where(wolf)?\sand\s)+(\.[\.\s]+\s?where\?)?/i - -GLOBAL_DATUM_INIT(is_color, /regex, regex("^#\[0-9a-fA-F]{6}$")) diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index c8fa54349e0f..e71e647eb487 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -57,12 +57,30 @@ GLOBAL_LIST_INIT(plasmaman_helmet_list, list( "Low" = "low")) //for icon making -> use "enviro" before this GLOBAL_LIST_EMPTY(ethereal_mark_list) //ethereal face marks -GLOBAL_LIST_INIT(color_list_ethereal,\ - list("O Class (Dark Green)" = "37833F", "O2 Class(Green)" = "97ee63", "O3 Class (Light Green)" = "00ff00",\ - "B Class (Blue)" = "3399ff", "A Class (Cyan)" = "00ffff", "F Class (White)" = "ffffff", "K Class (Yellow)" = "ffff00",\ - "M Class (Orange)" = "ff8700", "L Class (Red)" = "ff0000", "L2 Class (Dark Red)" = "9c3030", "T Class (Light Purple)" = "ff00ff",\ - "T2 Class (Dark Purple)" = "ee82ee")) -GLOBAL_LIST_INIT(color_list_preternis, list("Factory Default" = "FFFFFF", "Rust" = "B7410E", "Chrome" = "B0C4DE", "Overgrown" = "b2ee69", "Gunmetal Gray" = "8D918D", "Gold" = "D4AF37")) +GLOBAL_LIST_INIT(color_list_ethereal,list( + "O Class (Dark Green)" = "#37833F", + "O2 Class(Green)" = "#97ee63", + "O3 Class (Light Green)" = "#00ff00", + "B Class (Blue)" = "#3399ff", + "B1 Class (Dark Blue)" = "#6666ff", + "B2 Class (Faint Blue)" = "#b3d9ff", + "A Class (Cyan)" = "#00ffff", + "F Class (White)" = "#ffffff", + "K Class (Yellow)" = "#ffff00", + "M Class (Orange)" = "#ff8700", + "L Class (Red)" = "#ff0000", + "L1 Class (Faint Red)" = "#ff4d4d", + "L2 Class (Dark Red)" = "#9c3030", + "T Class (Light Purple)" = "#ff00ff", + "T2 Class (Dark Purple)" = "#ee82ee", + "White Dwarf" = "#f2f2f2",)) +GLOBAL_LIST_INIT(color_list_preternis, list( + "Factory Default" = "#FFFFFF", + "Rust" = "#B7410E", + "Chrome" = "#B0C4DE", + "Overgrown" = "#b2ee69", + "Gunmetal Gray" = "#8D918D", + "Gold" = "#D4AF37")) GLOBAL_LIST_EMPTY(pod_hair_list) //ethereal face marks GLOBAL_LIST_EMPTY(pod_flower_list) //ethereal face marks diff --git a/code/_globalvars/regexes.dm b/code/_globalvars/regexes.dm index bd252b68ce43..86c41cc7ab69 100644 --- a/code/_globalvars/regexes.dm +++ b/code/_globalvars/regexes.dm @@ -5,3 +5,4 @@ GLOBAL_DATUM_INIT(is_website, /regex, regex("http|www.|\[a-z0-9_-]+.(com|org|net GLOBAL_DATUM_INIT(is_email, /regex, regex("\[a-z0-9_-]+@\[a-z0-9_-]+.\[a-z0-9_-]+", "i")) GLOBAL_DATUM_INIT(is_alphanumeric, /regex, regex("\[a-z0-9]+", "i")) GLOBAL_DATUM_INIT(is_punctuation, /regex, regex("\[.!?]+", "i")) +GLOBAL_DATUM_INIT(is_color, /regex, regex("^#\[0-9a-fA-F]{6}$")) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index bd9ed1f4a8f9..c6d23ad8b245 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -654,7 +654,7 @@ screen_loc = ui_mood /atom/movable/screen/splash - icon = 'icons/blank_title.png' + icon = 'icons/blanks/blank_title.png' icon_state = "" screen_loc = "1,1" layer = SPLASHSCREEN_LAYER diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm index 9775303802c4..b9d618d4092d 100644 --- a/code/datums/components/forensics.dm +++ b/code/datums/components/forensics.dm @@ -99,7 +99,7 @@ if(!ignoregloves) H.gloves.add_fingerprint(H, TRUE) //ignoregloves = 1 to avoid infinite loop. return - var/full_print = md5(H.dna.uni_identity) + var/full_print = md5(H.dna.unique_identity) LAZYSET(fingerprints, full_print, full_print) return TRUE @@ -212,9 +212,9 @@ if(!iscarbon(M)) return var/mob/living/carbon/smelly = M - if(!smelly?.dna?.uni_identity) + if(!smelly?.dna?.unique_identity) return - var/smell_print = md5(smelly.dna.uni_identity) + var/smell_print = md5(smelly.dna.unique_identity) LAZYSET(scents, smell_print, smelly) return TRUE diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 252be124c5cf..3691cdd0c14f 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -326,7 +326,7 @@ G.fields["rank"] = assignment G.fields["age"] = H.age G.fields["species"] = H.dna.species.name - G.fields["fingerprint"] = md5(H.dna.uni_identity) + G.fields["fingerprint"] = md5(H.dna.unique_identity) G.fields["p_stat"] = "Active" G.fields["m_stat"] = "Stable" G.fields["gender"] = H.gender @@ -383,7 +383,7 @@ G.fields["gender"] = "Other" L.fields["blood_type"] = H.dna.blood_type L.fields["b_dna"] = H.dna.unique_enzymes - L.fields["identity"] = H.dna.uni_identity + L.fields["identity"] = H.dna.unique_identity L.fields["species"] = H.dna.species.type L.fields["features"] = H.dna.features L.fields["image"] = image diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm index 22972ce9e44d..c9beaeb322cb 100644 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ b/code/datums/diseases/advance/symptoms/skin.dm @@ -43,14 +43,14 @@ BONUS var/mob/living/carbon/human/H = M if(H.skin_tone == "albino") return - if(H.dna.features["mcolor"] == "EEE") + if(H.dna.features["mcolor"] == "#EEEEEE") return switch(A.stage) if(5) if(H.dna.species.use_skintones) H.skin_tone = "albino" else if(MUTCOLORS in H.dna.species.species_traits) - H.dna.features["mcolor"] = "EEE" //pure white. + H.dna.features["mcolor"] = "#EEEEEE" //pure white. H.regenerate_icons() else H.visible_message(span_warning("[H] looks a bit pale..."), span_notice("Your skin suddenly appears lighter...")) @@ -110,14 +110,14 @@ BONUS var/mob/living/carbon/human/H = M if(H.skin_tone == "african2") return - if(H.dna.features["mcolor"] == "000") + if(H.dna.features["mcolor"] == "#000000") return switch(A.stage) if(5) if(H.dna.species.use_skintones) H.skin_tone = "african2" else if(MUTCOLORS in H.dna.species.species_traits) - H.dna.features["mcolor"] = "000" //pure black. + H.dna.features["mcolor"] = "#000000" //pure black. H.regenerate_icons() else H.visible_message(span_warning("[H] looks a bit dark..."), span_notice("Your skin suddenly appears darker...")) diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm index 81cb105cda5e..c54724705a37 100644 --- a/code/datums/diseases/retrovirus.dm +++ b/code/datums/diseases/retrovirus.dm @@ -66,10 +66,13 @@ to_chat(affected_mob, span_danger("Your entire body vibrates.")) if (prob(35)) - if(prob(50)) - scramble_dna(affected_mob, 1, 0, rand(15,45)) - else - scramble_dna(affected_mob, 0, 1, rand(15,45)) + switch(rand(1,3)) + if(1) + scramble_dna(affected_mob, 1, 0, 0, rand(15,45)) + if(2) + scramble_dna(affected_mob, 0, 1, 0, rand(15,45)) + if(3) + scramble_dna(affected_mob, 0, 0, 1, rand(15,45)) if(4) if(restcure) @@ -78,7 +81,10 @@ cure() return if (prob(60)) - if(prob(50)) - scramble_dna(affected_mob, 1, 0, rand(50,75)) - else - scramble_dna(affected_mob, 0, 1, rand(50,75)) \ No newline at end of file + switch(rand(1,3)) + if(1) + scramble_dna(affected_mob, 1, 0, 0, rand(50,75)) + if(2) + scramble_dna(affected_mob, 0, 1, 0, rand(50,75)) + if(3) + scramble_dna(affected_mob, 0, 0, 1, rand(50,75)) diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 5e2f92b663ac..698a9cafc52c 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -1,21 +1,82 @@ +/** + * Some identity blocks (basically pieces of the unique_identity string variable of the dna datum, commonly abbreviated with ui) + * may have a length that differ from standard length of 3 ASCII characters. This list is necessary + * for these non-standard blocks to work, as well as the entire unique identity string. + * Should you add a new ui block which size differ from the standard (again, 3 ASCII characters), like for example, a color, + * please do not forget to also include it in this list in the following format: + * "[dna block number]" = dna block size, + * Failure to do that may result in bugs. Thanks. + */ +GLOBAL_LIST_INIT(identity_block_lengths, list( + "[DNA_HAIR_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + "[DNA_FACIAL_HAIR_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + "[DNA_EYE_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + )) + +/** + * The same rules of the above also apply here, with the exception that this is for the unique_features string variable + * (commonly abbreviated with uf) and its blocks. Both ui and uf have a standard block length of 3 ASCII characters. + */ +GLOBAL_LIST_INIT(features_block_lengths, list( + "[DNA_MUTANT_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + "[DNA_ETHEREAL_COLOR_BLOCK]" = DNA_BLOCK_SIZE_COLOR, + )) + +/** + * A list of numbers that keeps track of where ui blocks start in the unique_identity string variable of the dna datum. + * Commonly used by the datum/dna/set_uni_identity_block and datum/dna/get_uni_identity_block procs. + */ +GLOBAL_LIST_INIT(total_ui_len_by_block, populate_total_ui_len_by_block()) + +/proc/populate_total_ui_len_by_block() + . = list() + var/total_block_len = 1 + for(var/blocknumber in 1 to DNA_UNI_IDENTITY_BLOCKS) + . += total_block_len + total_block_len += GET_UI_BLOCK_LEN(blocknumber) + +///Ditto but for unique features. Used by the datum/dna/set_uni_feature_block and datum/dna/get_uni_feature_block procs. +GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) + +/proc/populate_total_uf_len_by_block() + . = list() + var/total_block_len = 1 + for(var/blocknumber in 1 to DNA_FEATURE_BLOCKS) + . += total_block_len + total_block_len += GET_UF_BLOCK_LEN(blocknumber) + /////////////////////////// DNA DATUM /datum/dna + ///An md5 hash of the dna holder's real name var/unique_enzymes - var/uni_identity + ///Stores the hashed values of traits such as skin tones, hair style, and gender + var/unique_identity var/blood_type - var/datum/species/species = new /datum/species/human //The type of mutant race the player is if applicable (i.e. potato-man) - var/list/features = list("FFF") //first value is mutant color - var/real_name //Stores the real name of the person who originally got this dna datum. Used primarely for changelings, - var/list/mutations = list() //All mutations are from now on here - var/list/temporary_mutations = list() //Temporary changes to the UE - var/list/previous = list() //For temporary name/ui/ue/blood_type modifications + ///The type of mutant race the player is if applicable (i.e. potato-man) + var/datum/species/species = new /datum/species/human + ///first value is mutant color + var/list/features = list("FFF") + ///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 primarely for changelings, + var/real_name + ///All mutations are from now on here + var/list/mutations = list() + ///Temporary changes to the UE + var/list/temporary_mutations = list() + ///For temporary name/ui/ue/blood_type modifications + var/list/previous = list() var/mob/living/holder - var/delete_species = TRUE //Set to FALSE when a body is scanned by a cloner to fix #38875 - var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block - var/default_mutation_genes[DNA_MUTATION_BLOCKS] //List of the default genes from this mutation to allow DNA Scanner highlighting + ///List of which mutations this carbon has and its assigned block + var/mutation_index[DNA_MUTATION_BLOCKS] + ///List of the default genes from this mutation to allow DNA Scanner highlighting + var/default_mutation_genes[DNA_MUTATION_BLOCKS] var/stability = 100 - var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers. + ///Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers. + var/scrambled = FALSE + + var/delete_species = TRUE //Set to FALSE when a body is scanned by a cloner to fix #38875 /datum/dna/New(mob/living/new_holder) if(istype(new_holder)) @@ -41,9 +102,10 @@ if(!istype(destination)) return destination.dna.unique_enzymes = unique_enzymes - destination.dna.uni_identity = uni_identity + destination.dna.unique_identity = unique_identity destination.dna.blood_type = blood_type destination.set_species(species.type, icon_update=0) + destination.dna.unique_features = unique_features destination.dna.features = features.Copy() destination.dna.real_name = real_name destination.dna.temporary_mutations = temporary_mutations.Copy() @@ -56,7 +118,8 @@ new_dna.unique_enzymes = unique_enzymes new_dna.mutation_index = mutation_index new_dna.default_mutation_genes = default_mutation_genes - new_dna.uni_identity = uni_identity + new_dna.unique_identity = unique_identity + new_dna.unique_features = unique_features new_dna.blood_type = blood_type new_dna.features = features.Copy() new_dna.species = new species.type @@ -71,9 +134,11 @@ mutation_type = HM.type if(get_mutation(mutation_type)) return + SEND_SIGNAL(holder, COMSIG_CARBON_GAIN_MUTATION, mutation_type, class) return force_give(new mutation_type (class, time, copymut = mutation)) /datum/dna/proc/remove_mutation(mutation_type) + SEND_SIGNAL(holder, COMSIG_CARBON_LOSE_MUTATION, mutation_type) return force_lose(get_mutation(mutation_type)) /datum/dna/proc/check_mutation(mutation_type) @@ -90,7 +155,7 @@ if((HM.class in classes) && !(HM.mutadone_proof && mutadone)) force_lose(HM) -/datum/dna/proc/generate_uni_identity() +/datum/dna/proc/generate_unique_identity() . = "" var/list/L = new /list(DNA_UNI_IDENTITY_BLOCKS) @@ -106,20 +171,63 @@ if(!GLOB.hair_styles_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/hair,GLOB.hair_styles_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list) L[DNA_HAIR_STYLE_BLOCK] = construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len) - L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color) + L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color, include_crunch = FALSE) if(!GLOB.facial_hair_styles_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) L[DNA_FACIAL_HAIR_STYLE_BLOCK] = construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len) - L[DNA_FACIAL_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.facial_hair_color) + L[DNA_FACIAL_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.facial_hair_color, include_crunch = FALSE) L[DNA_SKIN_TONE_BLOCK] = construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len) - L[DNA_EYE_COLOR_BLOCK] = sanitize_hexcolor(H.eye_color) + L[DNA_EYE_COLOR_BLOCK] = sanitize_hexcolor(H.eye_color, include_crunch = FALSE) - for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++) - if(L[i]) - . += L[i] - else - . += random_string(DNA_BLOCK_SIZE,GLOB.hex_characters) - return . + for(var/blocknum in 1 to DNA_UNI_IDENTITY_BLOCKS) + . += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters) + +/datum/dna/proc/generate_unique_features() + . = "" + + var/list/L = new /list(DNA_FEATURE_BLOCKS) + + if(features["mcolor"]) + L[DNA_MUTANT_COLOR_BLOCK] = sanitize_hexcolor(features["mcolor"], include_crunch = FALSE) + if(features["ethcolor"]) + L[DNA_ETHEREAL_COLOR_BLOCK] = sanitize_hexcolor(features["ethcolor"], include_crunch = FALSE) + if(features["body_markings"]) + L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len) + if(features["tail_lizard"]) + L[DNA_LIZARD_TAIL_BLOCK] = construct_block(GLOB.tails_list_lizard.Find(features["tail_lizard"]), GLOB.tails_list_lizard.len) + if(features["snout"]) + L[DNA_SNOUT_BLOCK] = construct_block(GLOB.snouts_list.Find(features["snout"]), GLOB.snouts_list.len) + if(features["horns"]) + L[DNA_HORNS_BLOCK] = construct_block(GLOB.horns_list.Find(features["horns"]), GLOB.horns_list.len) + if(features["frills"]) + L[DNA_FRILLS_BLOCK] = construct_block(GLOB.frills_list.Find(features["frills"]), GLOB.frills_list.len) + if(features["spines"]) + L[DNA_SPINES_BLOCK] = construct_block(GLOB.spines_list.Find(features["spines"]), GLOB.spines_list.len) + if(features["tail_human"]) + L[DNA_HUMAN_TAIL_BLOCK] = construct_block(GLOB.tails_list_human.Find(features["tail_human"]), GLOB.tails_list_human.len) + if(features["ears"]) + L[DNA_EARS_BLOCK] = construct_block(GLOB.ears_list.Find(features["ears"]), GLOB.ears_list.len) + if(features["moth_wings"] != "Burnt Off") + L[DNA_MOTH_WINGS_BLOCK] = construct_block(GLOB.moth_wings_list.Find(features["moth_wings"]), GLOB.moth_wings_list.len) + if(features["caps"]) + L[DNA_MUSHROOM_CAPS_BLOCK] = construct_block(GLOB.caps_list.Find(features["caps"]), GLOB.caps_list.len) + if(features["tail_polysmorph"]) + L[DNA_POLY_TAIL_BLOCK] = construct_block(GLOB.tails_list_polysmorph.Find(features["tail_polysmorph"]), GLOB.tails_list_polysmorph.len) + if(features["teeth"]) + L[DNA_POLY_TEETH_BLOCK] = construct_block(GLOB.teeth_list.Find(features["teeth"]), GLOB.teeth_list.len) + if(features["dome"]) + L[DNA_POLY_DOME_BLOCK] = construct_block(GLOB.dome_list.Find(features["dome"]), GLOB.dome_list.len) + if(features["dorsal_tubes"]) + L[DNA_POLY_DORSAL_BLOCK] = construct_block(GLOB.dorsal_tubes_list.Find(features["dorsal_tubes"]), GLOB.dorsal_tubes_list.len) + if(features["ethereal_mark"]) + L[DNA_ETHEREAL_MARK_BLOCK] = construct_block(GLOB.ethereal_mark_list.Find(features["ethereal_mark"]), GLOB.ethereal_mark_list.len) + if(features["pod_hair"]) + L[DNA_POD_HAIR_BLOCK] = construct_block(GLOB.pod_hair_list.Find(features["pod_hair"]), GLOB.pod_hair_list.len) + if(features["pod_flower"]) + L[DNA_POD_FLOWER_BLOCK] = construct_block(GLOB.pod_flower_list.Find(features["pod_flower"]), GLOB.pod_flower_list.len) + + for(var/blocknum in 1 to DNA_FEATURE_BLOCKS) + . += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters) /datum/dna/proc/generate_dna_blocks() var/bonus @@ -137,10 +245,17 @@ else mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE) default_mutation_genes[RACEMUT] = mutation_index[RACEMUT] - for(var/i in 2 to DNA_MUTATION_BLOCKS) - var/datum/mutation/human/M = mutations_temp[i] - mutation_index[M.type] = create_sequence(M.type, FALSE, M.difficulty) - default_mutation_genes[M.type] = mutation_index[M.type] + while(mutation_index.len < DNA_MUTATION_BLOCKS) + var/datum/mutation/human/mutation = mutations_temp[mutation_index.len] + var/conflict = FALSE + for(var/conflicting_mut in mutation.conflicts) + if(mutation_index.Find(conflicting_mut)) + conflict = TRUE //no more conflicting mutations in the same mob dna + mutations_temp.Cut(mutation_index.len, mutation_index.len+1) + if(conflict) + continue + mutation_index[mutation.type] = create_sequence(mutation.type, FALSE, mutation.difficulty) + default_mutation_genes[mutation.type] = mutation_index[mutation.type] shuffle_inplace(mutation_index) //Used to generate original gene sequences for every mutation @@ -163,8 +278,8 @@ if(active) return sequence while(difficulty) - var/randnum = rand(1, length_char(sequence)) - sequence = copytext_char(sequence, 1, randnum) + "X" + copytext_char(sequence, randnum + 1) + var/randnum = rand(1, length(sequence)) + sequence = copytext(sequence, 1, randnum) + "X" + copytext(sequence, randnum + 1) difficulty-- return sequence @@ -177,35 +292,90 @@ . += random_string(DNA_UNIQUE_ENZYMES_LEN, GLOB.hex_characters) return . +///Setter macro used to modify unique identity blocks. +/datum/dna/proc/set_uni_identity_block(blocknum, input) + var/precesing_blocks = copytext(unique_identity, 1, GLOB.total_ui_len_by_block[blocknum]) + var/succeeding_blocks = blocknum < GLOB.total_ui_len_by_block.len ? copytext(unique_identity, GLOB.total_ui_len_by_block[blocknum+1]) : "" + unique_identity = precesing_blocks + input + succeeding_blocks + +///Setter macro used to modify unique features blocks. +/datum/dna/proc/set_uni_feature_block(blocknum, input) + var/precesing_blocks = copytext(unique_features, 1, GLOB.total_uf_len_by_block[blocknum]) + var/succeeding_blocks = blocknum < GLOB.total_uf_len_by_block.len ? copytext(unique_features, GLOB.total_uf_len_by_block[blocknum+1]) : "" + unique_features = precesing_blocks + input + succeeding_blocks + /datum/dna/proc/update_ui_block(blocknumber) - if(!blocknumber || !ishuman(holder)) - return + if(!blocknumber) + CRASH("UI block index is null") + if(!ishuman(holder)) + CRASH("Non-human mobs shouldn't have DNA") var/mob/living/carbon/human/H = holder switch(blocknumber) if(DNA_HAIR_COLOR_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, sanitize_hexcolor(H.hair_color)) + set_uni_identity_block(blocknumber, sanitize_hexcolor(H.hair_color, include_crunch = FALSE)) if(DNA_FACIAL_HAIR_COLOR_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, sanitize_hexcolor(H.facial_hair_color)) + set_uni_identity_block(blocknumber, sanitize_hexcolor(H.facial_hair_color, include_crunch = FALSE)) if(DNA_SKIN_TONE_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len)) + set_uni_identity_block(blocknumber, construct_block(GLOB.skin_tones.Find(H.skin_tone), GLOB.skin_tones.len)) if(DNA_EYE_COLOR_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, sanitize_hexcolor(H.eye_color)) - var/obj/item/organ/eyes/eyes = H.getorgan(/obj/item/organ/eyes) - if(eyes) - eyes.old_eye_color = sanitize_hexcolor(H.eye_color) - eyes.eye_color = sanitize_hexcolor(H.eye_color) + set_uni_identity_block(blocknumber, sanitize_hexcolor(H.eye_color, include_crunch = FALSE)) if(DNA_GENDER_BLOCK) switch(H.gender) if(MALE) - uni_identity = setblock(uni_identity, blocknumber, construct_block(G_MALE, 3)) + set_uni_identity_block(blocknumber, construct_block(G_MALE, 3)) if(FEMALE) - uni_identity = setblock(uni_identity, blocknumber, construct_block(G_FEMALE, 3)) + set_uni_identity_block(blocknumber, construct_block(G_FEMALE, 3)) else - uni_identity = setblock(uni_identity, blocknumber, construct_block(G_PLURAL, 3)) + set_uni_identity_block(blocknumber, construct_block(G_PLURAL, 3)) if(DNA_FACIAL_HAIR_STYLE_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len)) + set_uni_identity_block(blocknumber, construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len)) if(DNA_HAIR_STYLE_BLOCK) - uni_identity = setblock(uni_identity, blocknumber, construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len)) + set_uni_identity_block(blocknumber, construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len)) + +/datum/dna/proc/update_uf_block(blocknumber) + if(!blocknumber) + CRASH("UF block index is null") + if(!ishuman(holder)) + CRASH("Non-human mobs shouldn't have DNA") + switch(blocknumber) + if(DNA_MUTANT_COLOR_BLOCK) + set_uni_feature_block(blocknumber, sanitize_hexcolor(features["mcolor"], include_crunch = FALSE)) + if(DNA_ETHEREAL_COLOR_BLOCK) + set_uni_feature_block(blocknumber, sanitize_hexcolor(features["ethcolor"], include_crunch = FALSE)) + if(DNA_LIZARD_MARKINGS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len)) + if(DNA_LIZARD_TAIL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_lizard.Find(features["tail_lizard"]), GLOB.tails_list_lizard.len)) + if(DNA_SNOUT_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.snouts_list.Find(features["snout"]), GLOB.snouts_list.len)) + if(DNA_HORNS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.horns_list.Find(features["horns"]), GLOB.horns_list.len)) + if(DNA_FRILLS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.frills_list.Find(features["frills"]), GLOB.frills_list.len)) + if(DNA_SPINES_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.spines_list.Find(features["spines"]), GLOB.spines_list.len)) + if(DNA_HUMAN_TAIL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_human.Find(features["tail_human"]), GLOB.tails_list_human.len)) + if(DNA_EARS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.ears_list.Find(features["ears"]), GLOB.ears_list.len)) + if(DNA_MOTH_WINGS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.moth_wings_list.Find(features["moth_wings"]), GLOB.moth_wings_list.len)) + if(DNA_MUSHROOM_CAPS_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.caps_list.Find(features["caps"]), GLOB.caps_list.len)) + if(DNA_POLY_TAIL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.tails_list_polysmorph.Find(features["tail_polysmorph"]), GLOB.tails_list_polysmorph.len)) + if(DNA_POLY_TEETH_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.teeth_list.Find(features["teeth"]), GLOB.teeth_list.len)) + if(DNA_POLY_DOME_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.dome_list.Find(features["dome"]), GLOB.dome_list.len)) + if(DNA_POLY_DORSAL_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.dorsal_tubes_list.Find(features["dorsal_tubes"]), GLOB.dorsal_tubes_list.len)) + if(DNA_ETHEREAL_MARK_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.ethereal_mark_list.Find(features["ethereal_mark"]), GLOB.ethereal_mark_list.len)) + if(DNA_POD_HAIR_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.pod_hair_list.Find(features["pod_hair"]), GLOB.pod_hair_list.len)) + if(DNA_POD_FLOWER_BLOCK) + set_uni_feature_block(blocknumber, construct_block(GLOB.pod_flower_list.Find(features["pod_flower"]), GLOB.pod_flower_list.len)) //Please use add_mutation or activate_mutation instead /datum/dna/proc/force_give(datum/mutation/human/HM) @@ -226,7 +396,7 @@ return /datum/dna/proc/is_same_as(datum/dna/D) - if(uni_identity == D.uni_identity && mutation_index == D.mutation_index && real_name == D.real_name) + if(unique_identity == D.unique_identity && mutation_index == D.mutation_index && real_name == D.real_name) if(species.type == D.species.type && features == D.features && blood_type == D.blood_type) return 1 return 0 @@ -259,17 +429,19 @@ //used to update dna UI, UE, and dna.real_name. /datum/dna/proc/update_dna_identity() - uni_identity = generate_uni_identity() + unique_identity = generate_unique_identity() unique_enzymes = generate_unique_enzymes() + unique_features = generate_unique_features() /datum/dna/proc/initialize_dna(newblood_type, skip_index = FALSE) if(newblood_type) blood_type = newblood_type unique_enzymes = generate_unique_enzymes() - uni_identity = generate_uni_identity() + unique_identity = generate_unique_identity() if(!skip_index) //I hate this generate_dna_blocks() features = random_features() + unique_features = generate_unique_features() /datum/dna/stored //subtype used by brain mob's stored_dna @@ -342,6 +514,7 @@ //Do not use force_transfer_mutations for stuff like cloners without some precautions, otherwise some conditional mutations could break (timers, drill hat etc) if(newfeatures) dna.features = newfeatures + dna.generate_unique_features() if(mrace) var/datum/species/newrace = new mrace.type @@ -356,7 +529,7 @@ dna.blood_type = newblood_type if(ui) - dna.uni_identity = ui + dna.unique_identity = ui updateappearance(icon_update=0) if(LAZYLEN(mutation_index)) @@ -390,7 +563,7 @@ if(!has_dna()) return - switch(deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 3)) + switch(deconstruct_block(get_uni_identity_block(dna.unique_identity, DNA_GENDER_BLOCK), 3)) if(G_MALE) gender = MALE if(G_FEMALE) @@ -400,18 +573,61 @@ /mob/living/carbon/human/updateappearance(icon_update=1, mutcolor_update=0, mutations_overlay_update=0) ..() - var/structure = dna.uni_identity - hair_color = sanitize_hexcolor(getblock(structure, DNA_HAIR_COLOR_BLOCK)) - facial_hair_color = sanitize_hexcolor(getblock(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)) - skin_tone = GLOB.skin_tones[deconstruct_block(getblock(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)] - eye_color = sanitize_hexcolor(getblock(structure, DNA_EYE_COLOR_BLOCK)) - facial_hair_style = GLOB.facial_hair_styles_list[deconstruct_block(getblock(structure, DNA_FACIAL_HAIR_STYLE_BLOCK), GLOB.facial_hair_styles_list.len)] + var/structure = dna.unique_identity + hair_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_HAIR_COLOR_BLOCK)) + facial_hair_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_FACIAL_HAIR_COLOR_BLOCK)) + skin_tone = GLOB.skin_tones[deconstruct_block(get_uni_identity_block(structure, DNA_SKIN_TONE_BLOCK), GLOB.skin_tones.len)] + eye_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_BLOCK)) + facial_hair_style = GLOB.facial_hair_styles_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIR_STYLE_BLOCK), GLOB.facial_hair_styles_list.len)] if(HAS_TRAIT(src, TRAIT_BALD)) - hair_style = "Bald" + hair_style = "Bald" else - hair_style = GLOB.hair_styles_list[deconstruct_block(getblock(structure, DNA_HAIR_STYLE_BLOCK), GLOB.hair_styles_list.len)] + hair_style = GLOB.hair_styles_list[deconstruct_block(get_uni_identity_block(structure, DNA_HAIR_STYLE_BLOCK), GLOB.hair_styles_list.len)] + var/features = dna.unique_features + if(dna.features["mcolor"]) + dna.features["mcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_MUTANT_COLOR_BLOCK)) + if(dna.features["ethcolor"]) + dna.features["ethcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_ETHEREAL_COLOR_BLOCK)) + if(dna.features["body_markings"]) + dna.features["body_markings"] = GLOB.body_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_MARKINGS_BLOCK), GLOB.body_markings_list.len)] + if(dna.features["tail_lizard"]) + dna.features["tail_lizard"] = GLOB.tails_list_lizard[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_TAIL_BLOCK), GLOB.tails_list_lizard.len)] + if(dna.features["snout"]) + dna.features["snout"] = GLOB.snouts_list[deconstruct_block(get_uni_feature_block(features, DNA_SNOUT_BLOCK), GLOB.snouts_list.len)] + if(dna.features["horns"]) + dna.features["horns"] = GLOB.horns_list[deconstruct_block(get_uni_feature_block(features, DNA_HORNS_BLOCK), GLOB.horns_list.len)] + if(dna.features["frills"]) + dna.features["frills"] = GLOB.frills_list[deconstruct_block(get_uni_feature_block(features, DNA_FRILLS_BLOCK), GLOB.frills_list.len)] + if(dna.features["spines"]) + dna.features["spines"] = GLOB.spines_list[deconstruct_block(get_uni_feature_block(features, DNA_SPINES_BLOCK), GLOB.spines_list.len)] + if(dna.features["tail_human"]) + dna.features["tail_human"] = GLOB.tails_list_human[deconstruct_block(get_uni_feature_block(features, DNA_HUMAN_TAIL_BLOCK), GLOB.tails_list_human.len)] + if(dna.features["ears"]) + dna.features["ears"] = GLOB.ears_list[deconstruct_block(get_uni_feature_block(features, DNA_EARS_BLOCK), GLOB.ears_list.len)] + if(dna.features["moth_wings"]) + var/genetic_value = GLOB.moth_wings_list[deconstruct_block(get_uni_feature_block(features, DNA_MOTH_WINGS_BLOCK), GLOB.moth_wings_list.len)] + dna.features["original_moth_wings"] = genetic_value + if(dna.features["moth_wings"] != "Burnt Off") + dna.features["moth_wings"] = genetic_value + if(dna.features["caps"]) + dna.features["caps"] = GLOB.caps_list[deconstruct_block(get_uni_feature_block(features, DNA_MUSHROOM_CAPS_BLOCK), GLOB.caps_list.len)] + // if(features["tail_polysmorph"]) + // dna.features["tail_polysmorph"] = GLOB.tails_list_polysmorph[deconstruct_block(get_uni_feature_block(features, DNA_POLY_TAIL_BLOCK), GLOB.tails_list_polysmorph.len)] + if(dna.features["teeth"]) + dna.features["teeth"] = GLOB.teeth_list[deconstruct_block(get_uni_feature_block(features, DNA_POLY_TEETH_BLOCK), GLOB.teeth_list.len)] + if(dna.features["dome"]) + dna.features["dome"] = GLOB.dome_list[deconstruct_block(get_uni_feature_block(features, DNA_POLY_DOME_BLOCK), GLOB.dome_list.len)] + if(dna.features["dorsal_tubes"]) + dna.features["dorsal_tubes"] = GLOB.dorsal_tubes_list[deconstruct_block(get_uni_feature_block(features, DNA_POLY_DORSAL_BLOCK), GLOB.dorsal_tubes_list.len)] + if(dna.features["ethereal_mark"]) + dna.features["ethereal_mark"] = GLOB.ethereal_mark_list[deconstruct_block(get_uni_feature_block(features, DNA_ETHEREAL_MARK_BLOCK), GLOB.ethereal_mark_list.len)] + if(dna.features["pod_hair"]) + dna.features["pod_hair"] = GLOB.pod_hair_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_HAIR_BLOCK), GLOB.pod_hair_list.len)] + if(dna.features["pod_flower"]) + dna.features["pod_flower"] = GLOB.pod_flower_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_FLOWER_BLOCK), GLOB.pod_flower_list.len)] + if(icon_update) - update_body() + dna.species.handle_body(src) // We want 'update_body_parts()' to be called only if mutcolor_update is TRUE, so no 'update_body()' here. update_hair() if(mutcolor_update) update_body_parts() @@ -443,7 +659,7 @@ //Return the active mutation of a type if there is one /datum/dna/proc/get_mutation(A) for(var/datum/mutation/human/HM in mutations) - if(HM.type == A) + if(istype(HM, A)) return HM /datum/dna/proc/check_block_string(mutation) @@ -479,22 +695,6 @@ /////////////////////////// DNA HELPER-PROCS ////////////////////////////// -/proc/getleftblocks(input,blocknumber,blocksize) - if(blocknumber > 1) - return copytext_char(input,1,((blocksize*blocknumber)-(blocksize-1))) - -/proc/getrightblocks(input,blocknumber,blocksize) - if(blocknumber < (length(input)/blocksize)) - return copytext_char(input,blocksize*blocknumber+1,length(input)+1) - -/proc/getblock(input, blocknumber, blocksize=DNA_BLOCK_SIZE) - return copytext_char(input, blocksize*(blocknumber-1)+1, (blocksize*blocknumber)+1) - -/proc/setblock(istring, blocknumber, replacement, blocksize=DNA_BLOCK_SIZE) - if(!istring || !blocknumber || !replacement || !blocksize) - return 0 - return getleftblocks(istring, blocknumber, blocksize) + replacement + getrightblocks(istring, blocknumber, blocksize) - /datum/dna/proc/mutation_in_sequence(mutation) if(!mutation) return @@ -506,15 +706,15 @@ return TRUE -/mob/living/carbon/proc/randmut(list/candidates, difficulty = 2) +/mob/living/carbon/proc/random_mutate(list/candidates, difficulty = 2) if(!has_dna()) - return + CRASH("[src] does not have DNA") var/mutation = pick(candidates) . = dna.add_mutation(mutation) -/mob/living/carbon/proc/easy_randmut(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE) +/mob/living/carbon/proc/easy_random_mutate(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE) if(!has_dna()) - return + CRASH("[src] does not have DNA") var/list/mutations = list() if(quality & POSITIVE) mutations += GLOB.good_mutations @@ -537,37 +737,47 @@ HM.scrambled = TRUE return TRUE -/mob/living/carbon/proc/randmuti() +/mob/living/carbon/proc/random_mutate_unique_identity() if(!has_dna()) - return + CRASH("[src] does not have DNA") var/num = rand(1, DNA_UNI_IDENTITY_BLOCKS) - var/newdna = setblock(dna.uni_identity, num, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) - dna.uni_identity = newdna + dna.set_uni_feature_block(num, random_string(GET_UI_BLOCK_LEN(num), GLOB.hex_characters)) updateappearance(mutations_overlay_update=1) +/mob/living/carbon/proc/random_mutate_unique_features() + if(!has_dna()) + CRASH("[src] does not have DNA") + var/num = rand(1, DNA_FEATURE_BLOCKS) + dna.set_uni_feature_block(num, random_string(GET_UF_BLOCK_LEN(num), GLOB.hex_characters)) + updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE) + /mob/living/carbon/proc/clean_dna() if(!has_dna()) - return + CRASH("[src] does not have DNA") dna.remove_all_mutations() -/mob/living/carbon/proc/clean_randmut(list/candidates, difficulty = 2) +/mob/living/carbon/proc/clean_random_mutate(list/candidates, difficulty = 2) clean_dna() - randmut(candidates, difficulty) + random_mutate(candidates, difficulty) -/proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, probability) +/proc/scramble_dna(mob/living/carbon/M, ui=FALSE, se=FALSE, uf=FALSE, probability) if(!M.has_dna()) - return 0 + CRASH("[M] does not have DNA") if(se) for(var/i=1, i<=DNA_MUTATION_BLOCKS, i++) if(prob(probability)) M.dna.generate_dna_blocks() M.domutcheck() if(ui) - for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++) + for(var/blocknum in 1 to DNA_UNI_IDENTITY_BLOCKS) + if(prob(probability)) + M.dna.set_uni_feature_block(blocknum, random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters)) + if(uf) + for(var/blocknum in 1 to DNA_FEATURE_BLOCKS) if(prob(probability)) - M.dna.uni_identity = setblock(M.dna.uni_identity, i, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters)) - M.updateappearance(mutations_overlay_update=1) - return 1 + M.dna.set_uni_feature_block(blocknum, random_string(GET_UF_BLOCK_LEN(blocknum), GLOB.hex_characters)) + if(ui || uf) + M.updateappearance(mutcolor_update=uf, mutations_overlay_update=1) //value in range 1 to values. values must be greater than 0 //all arguments assumed to be positive integers @@ -586,6 +796,12 @@ value = values return value +/proc/get_uni_identity_block(identity, blocknum) + return copytext(identity, GLOB.total_ui_len_by_block[blocknum], LAZYACCESS(GLOB.total_ui_len_by_block, blocknum+1)) + +/proc/get_uni_feature_block(features, blocknum) + return copytext(features, GLOB.total_uf_len_by_block[blocknum], LAZYACCESS(GLOB.total_uf_len_by_block, blocknum+1)) + /////////////////////////// DNA HELPER-PROCS /mob/living/carbon/human/proc/something_horrible(ignore_stability) diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index 1543df3d7b73..fea91f7f2b65 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -37,12 +37,15 @@ to_chat(owner, text_gain_indication) var/mob/new_mob if(prob(95)) - if(prob(50)) - new_mob = owner.easy_randmut(NEGATIVE + MINOR_NEGATIVE) - else - new_mob = owner.randmuti() + switch(rand(1,3)) + if(1) + new_mob = owner.easy_random_mutate(NEGATIVE + MINOR_NEGATIVE) + if(2) + new_mob = owner.random_mutate_unique_identity() + if(3) + new_mob = owner.random_mutate_unique_features() else - new_mob = owner.easy_randmut(POSITIVE) + new_mob = owner.easy_random_mutate(POSITIVE) if(new_mob && ismob(new_mob)) owner = new_mob . = owner @@ -195,6 +198,7 @@ var/obj/effect/dummy/luminescent_glow/glowth //shamelessly copied from luminescents var/glow = 3.5 var/range = 2.5 + var/glow_color var/current_nullify_timer // For veil yogstation\code\modules\antagonists\shadowling\shadowling_abilities.dm power_coeff = 1 conflicts = list(/datum/mutation/human/glow/anti) @@ -203,6 +207,7 @@ . = ..() if(.) return + glow_color = owner.dna.features["mcolor"] glowth = new(owner) modify() @@ -210,19 +215,8 @@ /datum/mutation/human/glow/modify() if(!glowth) return - - var/glow_color - - if(owner.dna.features["mcolor"][1] != "#") - //if it doesn't start with a pound, it needs that for the color - glow_color += "#" - if(length(owner.dna.features["mcolor"]) < 6) - //this atrocity converts shorthand hex rgb back into full hex that's required for light to be given a functional value - glow_color += owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][3] + owner.dna.features["mcolor"][3] - else - glow_color += owner.dna.features["mcolor"] - - glowth.set_light_range_power_color(range * GET_MUTATION_POWER(src), glow, glow_color) + var/power = GET_MUTATION_POWER(src) + glowth.set_light_range_power_color(range * power, glow * power, glow_color) /datum/mutation/human/glow/on_losing(mob/living/carbon/human/owner) . = ..() diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 1bf5b8e0c257..1bb19fd951ab 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -36,12 +36,13 @@ var/mob/living/carbon/human/H = L if(H.dna && !HAS_TRAIT(H, TRAIT_GENELESS)) if(prob(max(0,100-resist))) - H.randmuti() + H.random_mutate_unique_identity() + H.random_mutate_unique_features() if(prob(50)) if(prob(90)) - H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) + H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE) else - H.easy_randmut(POSITIVE) + H.easy_random_mutate(POSITIVE) H.domutcheck() L.rad_act(20) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index bf8621f5ef17..298aac00fe45 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -237,9 +237,9 @@ GLOBAL_VAR_INIT(clones, 0) var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) H.dna.remove_mutation_group(unclean_mutations) if(efficiency > 5 && prob(20)) - H.easy_randmut(POSITIVE) + H.easy_random_mutate(POSITIVE) if(efficiency < 3 && prob(50)) - var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) + var/mob/M = H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE) if(ismob(M)) H = M if((AGENDER || MGENDER || FGENDER) in H.dna.species.species_traits) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index f2de6d295cd9..625f67c8aa5f 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -554,7 +554,7 @@ R.fields["name"] = mob_occupant.real_name R.fields["id"] = copytext_char(md5(mob_occupant.real_name), 2, 6) R.fields["UE"] = dna.unique_enzymes - R.fields["UI"] = dna.uni_identity + R.fields["UI"] = dna.unique_identity R.fields["SE"] = dna.mutation_index R.fields["blood_type"] = dna.blood_type R.fields["features"] = dna.features @@ -594,7 +594,7 @@ var/datum/data/record/old_record = find_record("mindref", REF(mob_occupant.mind), records) if(body_only) old_record = find_record("UE", dna.unique_enzymes, records) //Body-only records cannot be identified by mind, so we use the DNA - if(old_record && ((old_record.fields["UI"] != dna.uni_identity) || (!old_record.fields["body_only"]))) //Never overwrite a mind-and-body record if it exists + if(old_record && ((old_record.fields["UI"] != dna.unique_identity) || (!old_record.fields["body_only"]))) //Never overwrite a mind-and-body record if it exists old_record = null if(old_record) records -= old_record diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index ea57402f9c34..7de000c1102d 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -34,6 +34,11 @@ /// Flag for the mutation ref search system. Search will include advanced injector mutations #define SEARCH_ADV_INJ 8 +#define ENZYME_COPY_BASE_COOLDOWN (60 SECONDS) + +#define RAD_PULSE_UNIQUE_IDENTITY "ui" +#define RAD_PULSE_UNIQUE_FEATURES "uf" + /obj/machinery/computer/scan_consolenew name = "DNA Console" desc = "Scan DNA." @@ -90,7 +95,11 @@ var/rad_pulse_index = 0 /// World time when the enzyme pulse should complete var/rad_pulse_timer = 0 - + /// Which dna string to edit with the pulse + var/rad_pulse_type + /// Cooldown for the genetic makeup transfer actions. + COOLDOWN_DECLARE(enzyme_copy_timer) + /// Used for setting tgui data - Whether the connected DNA Scanner is usable var/can_use_scanner = FALSE /// Used for setting tgui data - Whether the current DNA Scanner occupant is viable for genetic modification @@ -139,7 +148,7 @@ // This is for pulsing the UI element with radiation as part of genetic makeup // If rad_pulse_index > 0 then it means we're attempting a rad pulse - if((rad_pulse_index > 0) && (rad_pulse_timer <= world.time)) + if(((rad_pulse_index > 0) && (rad_pulse_timer <= world.time) && (rad_pulse_type == RAD_PULSE_UNIQUE_IDENTITY || rad_pulse_type == RAD_PULSE_UNIQUE_FEATURES))) rad_pulse() return @@ -290,7 +299,8 @@ data["subjectRads"] = scanner_occupant.radiation/(RAD_MOB_SAFE/100) data["subjectEnzymes"] = scanner_occupant.dna.unique_enzymes data["isMonkey"] = ismonkey(scanner_occupant) - data["subjectUNI"] = scanner_occupant.dna.uni_identity + data["subjectUNI"] = scanner_occupant.dna.unique_identity + data["subjectUF"] = scanner_occupant.dna.unique_features data["storage"]["occupant"] = tgui_occupant_mutations //data["subjectMutations"] = tgui_occupant_mutations else @@ -490,12 +500,12 @@ // X to allow highlighting logic to work on the tgui interface. if(newgene == "X") var/defaultseq = scanner_occupant.dna.default_mutation_genes[path] - defaultseq = copytext_char(defaultseq, 1, genepos) + newgene + copytext_char(defaultseq, genepos + 1) + defaultseq = copytext(defaultseq, 1, genepos) + newgene + copytext(defaultseq, genepos + 1) scanner_occupant.dna.default_mutation_genes[path] = defaultseq // Copy genome to scanner occupant and do some basic mutation checks as // we've increased the occupant rads - sequence = copytext_char(sequence, 1, genepos) + newgene + copytext_char(sequence, genepos + 1) + sequence = copytext(sequence, 1, genepos) + newgene + copytext(sequence, genepos + 1) scanner_occupant.dna.mutation_index[path] = sequence scanner_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected_scanner.damage_coeff scanner_occupant.domutcheck() @@ -1003,8 +1013,9 @@ // Set the new information genetic_makeup_buffer[buffer_index] = list( "label"="Slot [buffer_index]:[scanner_occupant.real_name]", - "UI"=scanner_occupant.dna.uni_identity, + "UI"=scanner_occupant.dna.unique_identity, "UE"=scanner_occupant.dna.unique_enzymes, + "UF"=scanner_occupant.dna.unique_features, "name"=scanner_occupant.real_name, "blood_type"=scanner_occupant.dna.blood_type) @@ -1051,6 +1062,7 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // "mixed" - Combination of both ue and ui if("makeup_injector") // Convert the index to a number and clamp within the array range, then @@ -1094,6 +1106,21 @@ I = new /obj/item/dnainjector/timed(loc) I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) + // If there is a connected scanner, we can use its upgrades to reduce + // the radiation generated by this injector + if(scanner_operational()) + I.damage_coeff = connected_scanner.damage_coeff + if("uf") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["name"] || !buffer_slot["UF"] || !buffer_slot["blood_type"]) + to_chat(usr,"Genetic data corrupted, unable to create injector.") + return + + I = new /obj/item/dnainjector/timed(loc) + I.fields = list("name"=buffer_slot["name"], "UF"=buffer_slot["UF"]) + // If there is a connected scanner, we can use its upgrades to reduce // the radiation generated by this injector if(scanner_operational()) @@ -1102,12 +1129,12 @@ // GUARD CHECK - There's currently no way to save partial genetic data. // However, if this is the case, we can't make a complete injector and // this catches that edge case - if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["UF"] || !buffer_slot["blood_type"]) to_chat(usr,span_warning("Genetic data corrupted, unable to create injector.")) return I = new /obj/item/dnainjector/timed(loc) - I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"]) + I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "UF"=buffer_slot["UF"], "blood_type"=buffer_slot["blood_type"]) // If there is a connected scanner, we can use its upgrades to reduce // the radiation generated by this injector @@ -1130,6 +1157,7 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // "mixed" - Combination of both ue and ui if("makeup_apply") // GUARD CHECK - Can we genetically modify the occupant? Includes scanner @@ -1167,6 +1195,7 @@ // Expected results: // "ue" - Unique Enzyme, changes name and blood type // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // "mixed" - Combination of both ue and ui if("makeup_delay") // Convert the index to a number and clamp within the array range, then @@ -1190,6 +1219,10 @@ // Attempts to modify the indexed element of the Unique Identity string // This is a time delayed action that is handled in process() // ---------------------------------------------------------------------- // + // params["type"] - Type of genetic makeup string to edit + // Expected results: + // "ui" - Unique Identity, changes looks + // "uf" - Unique Features, changes mutant bodyparts and mutcolors // params["index"] - The BYOND index of the Unique Identity string to // attempt to modify if("makeup_pulse") @@ -1198,9 +1231,16 @@ if(!can_modify_occupant()) return - // Set the appropriate timer and index to pulse. This is then managed + // Set the appropriate timer, string, and index to pulse. This is then managed // later on in process() - var/len = length_char(scanner_occupant.dna.uni_identity) + var/type = params["type"] + rad_pulse_type = type + var/len + switch(type) + if("ui") + len = length(scanner_occupant.dna.unique_identity) + if("uf") + len = length(scanner_occupant.dna.unique_features) rad_pulse_timer = world.time + (radduration*10) rad_pulse_index = WRAP(text2num(params["index"]), 1, len+1) begin_processing() @@ -1425,11 +1465,24 @@ if(!buffer_slot["UI"]) to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data.")) return FALSE - scanner_occupant.dna.uni_identity = buffer_slot["UI"] + scanner_occupant.dna.unique_identity = buffer_slot["UI"] scanner_occupant.updateappearance(mutations_overlay_update=1) scanner_occupant.radiation += rad_increase scanner_occupant.domutcheck() return TRUE + if("uf") + // GUARD CHECK - There's currently no way to save partial genetic data. + // However, if this is the case, we can't make a complete injector and + // this catches that edge case + if(!buffer_slot["UF"]) + to_chat(usr,"Genetic data corrupted, unable to apply genetic data.") + return FALSE + COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) + scanner_occupant.dna.unique_features = buffer_slot["UF"] + scanner_occupant.updateappearance(mutcolor_update=1, mutations_overlay_update=1) + scanner_occupant.radiation += rad_increase + scanner_occupant.domutcheck() + return TRUE if("ue") // GUARD CHECK - There's currently no way to save partial genetic data. // However, if this is the case, we can't make a complete injector and @@ -1437,6 +1490,7 @@ if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data.")) return FALSE + COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) scanner_occupant.real_name = buffer_slot["name"] scanner_occupant.name = buffer_slot["name"] scanner_occupant.dna.unique_enzymes = buffer_slot["UE"] @@ -1448,11 +1502,13 @@ // GUARD CHECK - There's currently no way to save partial genetic data. // However, if this is the case, we can't make a complete injector and // this catches that edge case - if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"]) + if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["UF"] || !buffer_slot["blood_type"]) to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data.")) return FALSE - scanner_occupant.dna.uni_identity = buffer_slot["UI"] - scanner_occupant.updateappearance(mutations_overlay_update=1) + COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN) + scanner_occupant.dna.unique_identity = buffer_slot["UI"] + scanner_occupant.dna.unique_features = buffer_slot["UF"] + scanner_occupant.updateappearance(mutcolor_update=1, mutations_overlay_update=1) scanner_occupant.real_name = buffer_slot["name"] scanner_occupant.name = buffer_slot["name"] scanner_occupant.dna.unique_enzymes = buffer_slot["UE"] @@ -1944,20 +2000,36 @@ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner // operational guard checks. // If we can't, abort the procedure. - if(!can_modify_occupant()) + if(!can_modify_occupant()|| (rad_pulse_type != RAD_PULSE_UNIQUE_IDENTITY && rad_pulse_type != RAD_PULSE_UNIQUE_FEATURES)) rad_pulse_index = 0 end_processing() return - var/len = length_char(scanner_occupant.dna.uni_identity) + var/len + switch(rad_pulse_type) + if(RAD_PULSE_UNIQUE_IDENTITY) + len = length(scanner_occupant.dna.unique_identity) + if(RAD_PULSE_UNIQUE_FEATURES) + len = length(scanner_occupant.dna.unique_features) + var/num = randomize_radiation_accuracy(rad_pulse_index, radduration + (connected_scanner.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2 //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low - var/hex = copytext_char(scanner_occupant.dna.uni_identity, num, num+1) + var/hex + switch(rad_pulse_type) + if(RAD_PULSE_UNIQUE_IDENTITY) + hex = copytext(scanner_occupant.dna.unique_identity, num, num+1) + if(RAD_PULSE_UNIQUE_FEATURES) + hex = copytext(scanner_occupant.dna.unique_features, num, num+1) hex = scramble(hex, radstrength, radduration) - scanner_occupant.dna.uni_identity = copytext_char(scanner_occupant.dna.uni_identity, 1, num) + hex + copytext_char(scanner_occupant.dna.uni_identity, num + 1) - scanner_occupant.updateappearance(mutations_overlay_update=1) + switch(rad_pulse_type) + if(RAD_PULSE_UNIQUE_IDENTITY) + scanner_occupant.dna.unique_identity = copytext(scanner_occupant.dna.unique_identity, 1, num) + hex + copytext(scanner_occupant.dna.unique_identity, num + 1) + if(RAD_PULSE_UNIQUE_FEATURES) + scanner_occupant.dna.unique_features = copytext(scanner_occupant.dna.unique_features, 1, num) + hex + copytext(scanner_occupant.dna.unique_features, num + 1) + scanner_occupant.updateappearance(mutcolor_update=1, mutations_overlay_update=1) rad_pulse_index = 0 + rad_pulse_type = null end_processing() return diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm index 29d959fea8ae..00fe2a5bc253 100644 --- a/code/game/machinery/exp_cloner.dm +++ b/code/game/machinery/exp_cloner.dm @@ -26,9 +26,9 @@ var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) H.dna.remove_mutation_group(unclean_mutations) if(efficiency > 5 && prob(20)) - H.easy_randmut(POSITIVE) + H.easy_random_mutate(POSITIVE) if(efficiency < 3 && prob(50)) - var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) + var/mob/M = H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE) if(ismob(M)) H = M @@ -298,6 +298,6 @@ temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else - pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, clone_species, dna.features, mob_occupant.faction) + pod.growclone(mob_occupant.real_name, dna.unique_identity, dna.mutation_index, null, null, clone_species, dna.features, mob_occupant.faction) temp = "[mob_occupant.real_name] => Cloning data sent to pod." playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm index 16f24abb5e66..575b113d92c6 100644 --- a/code/game/objects/items/cosmetics.dm +++ b/code/game/objects/items/cosmetics.dm @@ -262,7 +262,7 @@ if(!new_grad_style) return - var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference","#"+human_target.grad_color) as color|null + var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference",human_target.grad_color) as color|null if(!new_grad_color) return diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index 5b82cf6d7ef2..6bc297d91ea4 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -41,8 +41,11 @@ M.name = M.real_name M.dna.blood_type = fields["blood_type"] if(fields["UI"]) //UI+UE - M.dna.uni_identity = merge_text(M.dna.uni_identity, fields["UI"]) - M.updateappearance(mutations_overlay_update=1) + M.dna.unique_identity = merge_text(M.dna.unique_identity, fields["UI"]) + if(fields["UF"]) + M.dna.unique_features = merge_text(M.dna.unique_features, fields["UF"]) + if(fields["UI"] || fields["UF"]) + M.updateappearance(mutcolor_update=1, mutations_overlay_update=1) log_attack("[log_msg] [loc_name(user)]") return TRUE return FALSE @@ -505,10 +508,16 @@ M.dna.temporary_mutations[UE_CHANGED] = endtime if(fields["UI"]) //UI+UE if(!M.dna.previous["UI"]) - M.dna.previous["UI"] = M.dna.uni_identity - M.dna.uni_identity = merge_text(M.dna.uni_identity, fields["UI"]) - M.updateappearance(mutations_overlay_update=1) + M.dna.previous["UI"] = M.dna.unique_identity + M.dna.unique_identity = merge_text(M.dna.unique_identity, fields["UI"]) M.dna.temporary_mutations[UI_CHANGED] = endtime + if(fields["UF"]) //UI+UE + if(!M.dna.previous["UF"]) + M.dna.previous["UF"] = M.dna.unique_features + M.dna.unique_features = merge_text(M.dna.unique_features, fields["UF"]) + M.dna.temporary_mutations[UF_CHANGED] = endtime + if(fields["UI"] || fields["UF"]) + M.updateappearance(mutcolor_update=1, mutations_overlay_update=1) log_attack("[log_msg] [loc_name(user)]") return TRUE else diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 5dcb1d361bba..92c92d3ae602 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -39,7 +39,7 @@ /obj/structure/mirror/proc/preapply_choices(selectiontype, mob/living/carbon/human/H) switch(selectiontype) if(FACE_HAIR_COLOR) - var/new_hair_color = input(H, "Choose your face hair color", "Face Hair Color","#"+H.facial_hair_color) as color|null + var/new_hair_color = input(H, "Choose your face hair color", "Face Hair Color",H.facial_hair_color) as color|null if(!new_hair_color) return TRUE H.facial_hair_color = sanitize_hexcolor(new_hair_color) @@ -47,7 +47,7 @@ H.update_hair() return TRUE if(HAIR_COLOR) - var/new_hair_color = input(H, "Choose your hair color", "Hair Color","#"+H.hair_color) as color|null + var/new_hair_color = input(H, "Choose your hair color", "Hair Color",H.hair_color) as color|null if(!new_hair_color) return TRUE H.hair_color = sanitize_hexcolor(new_hair_color) @@ -185,7 +185,7 @@ . = ..() switch(selectiontype) if(EYE_COLOR) - var/new_eye_color = input(H, "Choose your eye color", "Eye Color","#"+H.eye_color) as color|null + var/new_eye_color = input(H, "Choose your eye color", "Eye Color",H.eye_color) as color|null if(!new_eye_color) return TRUE H.eye_color = sanitize_hexcolor(new_eye_color) @@ -204,12 +204,13 @@ H.mind.name = newname return TRUE if(MUTANT_COLOR) - var/new_mutantcolor = input(H, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null + var/new_mutantcolor = input(H, "Choose your skin color:", "Race change",H.dna.features["mcolor"]) as color|null if(!new_mutantcolor) return TRUE var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright + if(ReadHSV(temp_hsv)[3] >= ReadHSV("#3a3a3a")[3]) // mutantcolors must be bright H.dna.features["mcolor"] = sanitize_hexcolor(new_mutantcolor) + H.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK) else to_chat(H, span_notice("Invalid color. Your color is not bright enough.")) H.update_body() diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index a2e6ba03b463..4aa999d19ab2 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -63,7 +63,7 @@ /turf/closed/indestructible/splashscreen name = "Space Station 13" - icon = 'icons/blank_title.png' + icon = 'icons/blanks/blank_title.png' icon_state = "" layer = FLY_LAYER bullet_bounce_sound = null diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index f1c69425f798..d75c7b3274eb 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -10,38 +10,38 @@ user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475") -/proc/randomize_human(mob/living/carbon/human/H) - H.gender = pick(MALE, FEMALE) - H.real_name = random_unique_name(H.gender) - H.name = H.real_name - H.underwear = random_underwear(H.gender) - H.skin_tone = random_skin_tone() - H.hair_style = random_hair_style(H.gender) - H.facial_hair_style = random_facial_hair_style(H.gender) - H.hair_color = random_short_color() - H.facial_hair_color = H.hair_color - H.eye_color = random_eye_color() - H.dna.blood_type = random_blood_type() +/proc/randomize_human(mob/living/carbon/human/human) + human.gender = pick(MALE, FEMALE) + human.real_name = human.dna?.species.random_name(human.gender) || random_unique_name(human.gender) + human.name = human.real_name + human.underwear = random_underwear(human.gender) + human.skin_tone = random_skin_tone() + human.hair_style = random_hair_style(human.gender) + human.facial_hair_style = random_facial_hair_style(human.gender) + human.hair_color = "#[random_color()]" + human.facial_hair_color = human.hair_color + human.eye_color = random_eye_color() + human.dna.blood_type = random_blood_type() // Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant. - H.dna.features["mcolor"] = random_short_color() - H.dna.features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)] - H.dna.features["pretcolor"] = GLOB.color_list_preternis[pick(GLOB.color_list_preternis)] - H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard) - H.dna.features["tail_polysmorph"] = pick(GLOB.tails_list_polysmorph) - H.dna.features["snout"] = pick(GLOB.snouts_list) - H.dna.features["horns"] = pick(GLOB.horns_list) - H.dna.features["frills"] = pick(GLOB.frills_list) - H.dna.features["spines"] = pick(GLOB.spines_list) - H.dna.features["body_markings"] = pick(GLOB.body_markings_list) - H.dna.features["moth_wings"] = pick(GLOB.moth_wings_list) - H.dna.features["teeth"] = pick(GLOB.teeth_list) - H.dna.features["dome"] = pick(GLOB.dome_list) - H.dna.features["dorsal_tubes"] = pick(GLOB.dorsal_tubes_list) - H.dna.features["ethereal_mark"] = pick(GLOB.ethereal_mark_list) - H.dna.features["pod_hair"] = pick(GLOB.pod_hair_list) - H.dna.features["pod_flower"] = GLOB.pod_flower_list[H.dna.features["pod_hair"]] + human.dna.features["mcolor"] = "#[random_color()]" + human.dna.features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)] + human.dna.features["pretcolor"] = GLOB.color_list_preternis[pick(GLOB.color_list_preternis)] + human.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard) + human.dna.features["tail_polysmorph"] = pick(GLOB.tails_list_polysmorph) + human.dna.features["snout"] = pick(GLOB.snouts_list) + human.dna.features["horns"] = pick(GLOB.horns_list) + human.dna.features["frills"] = pick(GLOB.frills_list) + human.dna.features["spines"] = pick(GLOB.spines_list) + human.dna.features["body_markings"] = pick(GLOB.body_markings_list) + human.dna.features["moth_wings"] = pick(GLOB.moth_wings_list) + human.dna.features["teeth"] = pick(GLOB.teeth_list) + human.dna.features["dome"] = pick(GLOB.dome_list) + human.dna.features["dorsal_tubes"] = pick(GLOB.dorsal_tubes_list) + human.dna.features["ethereal_mark"] = pick(GLOB.ethereal_mark_list) + human.dna.features["pod_hair"] = pick(GLOB.pod_hair_list) + human.dna.features["pod_flower"] = GLOB.pod_flower_list[human.dna.features["pod_hair"]] - H.update_body() - H.update_hair() - H.update_body_parts() + human.update_body() + human.update_hair() + human.update_body_parts() diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index a701a07d8372..6afba9e29fa4 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -248,7 +248,7 @@ dat += "
| Name | Fingerprints |
|---|---|
| [H] | [md5(H.dna.uni_identity)] |
| [H] | [md5(H.dna.unique_identity)] |