Reduces species feature boilerplate (#93570)

## About The Pull Request

Rather than having 100 separate list variables on `SSaccessories`, have
1 list for all feature keys that are associated with sprite accessories

This way you can get a feature by doing
`SSaccessories.feature_list[key]`, instead of necessitating
`SSaccessories.ears_list`, `SSaccessories.tail_list`, etc.

This lets us cut back on a lot of boilerplate in prefs, dna, and organs

## Why It's Good For The Game

We can see the benefit in this example: This is all the code for horn
DNA, bodypart overlay, and preference
```dm
/datum/dna_block/feature/accessory/horn
	feature_key = FEATURE_HORNS
```
```dm
/datum/bodypart_overlay/mutant/horns
	layers = EXTERNAL_ADJACENT
	feature_key = FEATURE_HORNS
	dyable = TRUE

/datum/bodypart_overlay/mutant/horns/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
	return !(bodypart_owner.owner?.obscured_slots & HIDEHAIR)
```
```dm
/datum/preference/choiced/species_feature/lizard_horns
	savefile_key = "feature_lizard_horns"
	savefile_identifier = PREFERENCE_CHARACTER
	category = PREFERENCE_CATEGORY_FEATURES
	main_feature_name = "Horns"
	should_generate_icons = TRUE
	relevant_organ = /obj/item/organ/horns

/datum/preference/choiced/species_feature/lizard_horns/icon_for(value)
	return generate_lizard_side_shot(get_accessory_for_value(value), "horns")
```

## Changelog

🆑 Melbert
refactor: Refactored species unique organs slightly, particularly how
they are set up at game start. Report any oddities, like invisible tails
or wings
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
MrMelbert
2025-10-29 21:46:32 -05:00
committed by GitHub
parent 340f9d5fe8
commit 585b02c325
30 changed files with 181 additions and 374 deletions

View File

@@ -73,6 +73,7 @@
// Other
#define FEATURE_WINGS "wings"
#define FEATURE_WINGS_OPEN "[FEATURE_WINGS]_open"
#define FEATURE_TAIL_MONKEY "tail_monkey"
#define FEATURE_TAIL_XENO "tail_xeno"
#define FEATURE_TAILSPINES "tailspines" // Different from regular spines, these appear on tails

View File

@@ -170,7 +170,7 @@ GLOBAL_LIST_INIT(dna_identity_blocks, init_identity_block_types())
/proc/init_identity_block_types()
. = list()
for(var/datum/dna_block/identity/block_path as anything in subtypesof(/datum/dna_block/identity))
for(var/datum/dna_block/identity/block_path as anything in valid_subtypesof(/datum/dna_block/identity))
var/datum/dna_block/identity/new_block = new block_path()
.[block_path] = new_block
@@ -179,6 +179,6 @@ GLOBAL_LIST_INIT(dna_feature_blocks, init_feature_block_types())
/proc/init_feature_block_types()
. = list()
for(var/datum/dna_block/feature/block_path as anything in subtypesof(/datum/dna_block/feature))
for(var/datum/dna_block/feature/block_path as anything in valid_subtypesof(/datum/dna_block/feature))
var/datum/dna_block/feature/new_block = new block_path()
.[block_path] = new_block

View File

@@ -5,6 +5,11 @@
/// The female specific list that we get from init_sprite_accessory_subtypes()
#define FEMALE_SPRITE_LIST "female_sprites"
/// Use this to init a sprite accessory list for a feature where mobs are required to have one selected
#define INIT_ACCESSORY(sprite_accessory) init_sprite_accessory_subtypes(sprite_accessory, add_blank = FALSE)[DEFAULT_SPRITE_LIST]
/// Use this to init a sprite accessory list for a feature where mobs can opt to not have one selected
#define INIT_OPTIONAL_ACCESSORY(sprite_accessory) init_sprite_accessory_subtypes(sprite_accessory, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
/// subsystem that just holds lists of sprite accessories for accession in generating said sprites.
/// A sprite accessory is something that we add to a human sprite to make them look different. This is hair, facial hair, underwear, mutant bits, etc.
SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
@@ -36,28 +41,8 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
//Socks
var/list/socks_list //! stores /datum/sprite_accessory/socks indexed by name
//Lizard Bits (all datum lists indexed by name)
var/list/lizard_markings_list
var/list/snouts_list
var/list/horns_list
var/list/frills_list
var/list/spines_list
var/list/tail_spines_list
//Mutant Human bits
var/list/tails_list_felinid
var/list/tails_list_lizard
var/list/tails_list_monkey
var/list/tails_list_xeno
var/list/tails_list_fish
var/list/ears_list
var/list/wings_list
var/list/wings_open_list
var/list/moth_wings_list
var/list/moth_antennae_list
var/list/moth_markings_list
var/list/caps_list
var/list/pod_hair_list
//All features, indexed by feature key, then name of the sprite accessory to the datum iteslf
var/list/list/feature_list
/datum/controller/subsystem/accessories/PreInit() // this stuff NEEDS to be set up before GLOB for preferences and stuff to work so this must go here. sorry
setup_lists()
@@ -91,26 +76,31 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
socks_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/socks)[DEFAULT_SPRITE_LIST]
lizard_markings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/lizard_markings, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_felinid = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/felinid, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_lizard = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard)[DEFAULT_SPRITE_LIST]
tails_list_monkey = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/monkey)[DEFAULT_SPRITE_LIST]
tails_list_xeno = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/xeno)[DEFAULT_SPRITE_LIST]
//tails fo fish organ infusions, not for prefs.
tails_list_fish = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/fish)[DEFAULT_SPRITE_LIST]
snouts_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts)[DEFAULT_SPRITE_LIST]
horns_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
ears_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
wings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
wings_open_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/wings_open)[DEFAULT_SPRITE_LIST]
frills_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
spines_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tail_spines_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/tail_spines, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
caps_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/caps)[DEFAULT_SPRITE_LIST]
moth_wings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings)[DEFAULT_SPRITE_LIST]
moth_antennae_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_antennae)[DEFAULT_SPRITE_LIST]
moth_markings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_markings, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
pod_hair_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/pod_hair)[DEFAULT_SPRITE_LIST]
feature_list = list()
// felinids
feature_list[FEATURE_TAIL_CAT] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/tails/felinid)
feature_list[FEATURE_EARS] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/ears)
// lizards
feature_list[FEATURE_FRILLS] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/frills)
feature_list[FEATURE_HORNS] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/horns)
feature_list[FEATURE_LIZARD_MARKINGS] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/lizard_markings)
feature_list[FEATURE_SNOUT] = INIT_ACCESSORY(/datum/sprite_accessory/snouts)
feature_list[FEATURE_SPINES] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/spines)
feature_list[FEATURE_TAILSPINES] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/tail_spines)
feature_list[FEATURE_TAIL_LIZARD] = INIT_ACCESSORY(/datum/sprite_accessory/tails/lizard)
// moths
feature_list[FEATURE_MOTH_WINGS] = INIT_ACCESSORY(/datum/sprite_accessory/moth_wings)
feature_list[FEATURE_MOTH_ANTENNAE] = INIT_ACCESSORY(/datum/sprite_accessory/moth_antennae)
feature_list[FEATURE_MOTH_MARKINGS] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/moth_markings)
// generic wings
feature_list[FEATURE_WINGS] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/wings)
feature_list[FEATURE_WINGS_OPEN] = INIT_OPTIONAL_ACCESSORY(/datum/sprite_accessory/wings_open)
// generic features
feature_list[FEATURE_MUSH_CAP] = INIT_ACCESSORY(/datum/sprite_accessory/caps)
feature_list[FEATURE_POD_HAIR] = INIT_ACCESSORY(/datum/sprite_accessory/pod_hair)
feature_list[FEATURE_TAIL_FISH] = INIT_ACCESSORY(/datum/sprite_accessory/tails/fish)
feature_list[FEATURE_TAIL_MONKEY] = INIT_ACCESSORY(/datum/sprite_accessory/tails/monkey)
feature_list[FEATURE_TAIL_XENO] = INIT_ACCESSORY(/datum/sprite_accessory/tails/xeno)
/// This proc just initializes all /datum/sprite_accessory/hair_gradient into an list indexed by gradient-style name
/datum/controller/subsystem/accessories/proc/init_hair_gradients()
@@ -161,6 +151,9 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
return returnable_list
#undef INIT_ACCESSORY
#undef INIT_OPTIONAL_ACCESSORY
#undef DEFAULT_SPRITE_LIST
#undef MALE_SPRITE_LIST
#undef FEMALE_SPRITE_LIST

View File

@@ -42,15 +42,12 @@
var/gender_string = (use_gender && limb.is_dimorphic) ? (limb.gender == MALE ? MALE : FEMALE + "_") : "" //we only got male and female sprites
return mutable_appearance(icon, gender_string + icon_state + "_" + limb.body_zone, layer = layer)
/datum/bodypart_overlay/simple/body_marking/get_accessory(name)
return SSaccessories.feature_list[dna_feature_key][name]
/datum/bodypart_overlay/simple/body_marking/moth
dna_feature_key = FEATURE_MOTH_MARKINGS
/datum/bodypart_overlay/simple/body_marking/moth/get_accessory(name)
return SSaccessories.moth_markings_list[name]
/datum/bodypart_overlay/simple/body_marking/lizard
dna_feature_key = FEATURE_LIZARD_MARKINGS
applies_to = list(/obj/item/bodypart/chest)
/datum/bodypart_overlay/simple/body_marking/lizard/get_accessory(name)
return SSaccessories.lizard_markings_list[name]

View File

@@ -119,7 +119,11 @@
///Return a dumb glob list for this specific feature (called from parse_sprite)
/datum/bodypart_overlay/mutant/proc/get_global_feature_list()
CRASH("External organ has no feature list, it will render invisible")
var/list/feature_list = SSaccessories.feature_list[feature_key]
if(isnull(feature_list))
stack_trace("External organ has no feature list, it will render invisible")
return list()
return feature_list
///Give the organ its color. Force will override the existing one.
/datum/bodypart_overlay/mutant/proc/inherit_color(obj/item/bodypart/bodypart_owner, force)

View File

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

View File

@@ -56,12 +56,14 @@
/// Blocks for unique identities (skin tones, hair style, and gender)
/datum/dna_block/identity
abstract_type = /datum/dna_block/identity
/datum/dna_block/identity/position_in_hash()
return GLOB.total_ui_len_by_block[type]
/// Blocks for unique features (mutant color, mutant bodyparts)
/datum/dna_block/feature
abstract_type = /datum/dna_block/feature
/// The feature key this block ties in to.
var/feature_key = null

View File

@@ -220,7 +220,7 @@
greyscale_colors = FISH_ORGAN_COLOR
bodypart_overlay = /datum/bodypart_overlay/mutant/tail/fish
dna_block = /datum/dna_block/feature/tail_fish
dna_block = /datum/dna_block/feature/accessory/tail_fish
wag_flags = NONE
organ_traits = list(TRAIT_FLOPPING, TRAIT_SWIMMER)
restyle_flags = EXTERNAL_RESTYLE_FLESH
@@ -288,9 +288,9 @@
/datum/bodypart_overlay/mutant/tail/fish/on_mob_insert(obj/item/organ/parent, mob/living/carbon/receiver)
//Initialize the related dna feature block if we don't have any so it doesn't error out.
//This isn't tied to any species, but I kinda want it to be mutable instead of having a fixed sprite accessory.
if(imprint_on_next_insertion && !receiver.dna.features[FEATURE_TAIL_FISH])
receiver.dna.features[FEATURE_TAIL_FISH] = pick(SSaccessories.tails_list_fish)
receiver.dna.update_uf_block(/datum/dna_block/feature/tail_fish)
if(imprint_on_next_insertion && !receiver.dna.features[feature_key])
receiver.dna.features[feature_key] = pick(SSaccessories.feature_list[feature_key])
receiver.dna.update_uf_block(/datum/dna_block/feature/accessory/tail_fish)
return ..()
@@ -301,9 +301,6 @@
else //otherwise get one from a set of faded out blue and some greys colors.
return pick("#B4B8DD", "#85C7D0", "#67BBEE", "#2F4450", "#55CCBB", "#999FD0", "#345066", "#585B69", "#7381A0", "#B6DDE5", "#4E4E50")
/datum/bodypart_overlay/mutant/tail/fish/get_global_feature_list()
return SSaccessories.tails_list_fish
/datum/bodypart_overlay/mutant/tail/fish/get_image(image_layer, obj/item/bodypart/limb)
var/mutable_appearance/appearance = ..()
// We add all appearances the parent bodypart has to the tail to inherit scales and fancy effects

View File

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

View File

@@ -109,7 +109,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
/// If the selected species has this in its /datum/species/body_markings,
/// will show the feature as selectable.
var/relevant_body_markings = null
var/datum/bodypart_overlay/simple/body_marking/relevant_body_markings = null
/// If the selected species has this in its /datum/species/inherent_traits,
/// will show the feature as selectable.
@@ -117,7 +117,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
/// If the selected species has this in its /datum/species/var/external_organs,
/// will show the feature as selectable.
var/relevant_organ = null
var/obj/item/organ/relevant_organ = null
/// If the selected species has this head_flag by default,
/// will show the feature as selectable.
@@ -443,6 +443,42 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
return data
/// This subtype handles a lot of boilerplate for implementing a species preference tied to a feature key / sprite accessory
/datum/preference/choiced/species_feature
abstract_type = /datum/preference/choiced/species_feature
/// What feature key does this feature represent?
/// Does not need to be set, it will infer it from either relevant_organ or relevant_body_markings.
/// However you can set it manually if you have a more complex feature.
var/feature_key
/datum/preference/choiced/species_feature/New()
. = ..()
if(relevant_organ && relevant_organ::bodypart_overlay)
feature_key ||= relevant_organ::bodypart_overlay::feature_key
main_feature_name ||= capitalize(relevant_organ::name)
if(relevant_body_markings)
feature_key ||= relevant_body_markings::dna_feature_key
main_feature_name ||= "Body markings"
if(isnull(feature_key))
CRASH("`feature_key` was not set or inferable for [type]!")
/datum/preference/choiced/species_feature/init_possible_values()
return assoc_to_keys_features(get_accessory_list())
/datum/preference/choiced/species_feature/create_default_value()
return get_consistent_feature_entry(get_accessory_list())
/datum/preference/choiced/species_feature/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[feature_key] = value
/// Returns what acessory list to draw from
/datum/preference/choiced/species_feature/proc/get_accessory_list() as /list
return SSaccessories.feature_list[feature_key]
/// Get a specific accessory for a given value
/datum/preference/choiced/species_feature/proc/get_accessory_for_value(value)
return get_accessory_list()[value]
/// A preference that represents an RGB color of something.
/// Will give the value as 6 hex digits, without a hash.
/datum/preference/color

View File

@@ -1,32 +1,13 @@
/datum/preference/choiced/tail_felinid
/datum/preference/choiced/species_feature/tail_felinid
savefile_key = "feature_human_tail" //savefile keys cannot be changed, blame whoever named them this way.
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
can_randomize = FALSE
relevant_organ = /obj/item/organ/tail/cat
/datum/preference/choiced/tail_felinid/init_possible_values()
return assoc_to_keys_features(SSaccessories.tails_list_felinid)
/datum/preference/choiced/tail_felinid/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_TAIL_CAT] = value
/datum/preference/choiced/tail_felinid/create_default_value()
var/datum/sprite_accessory/tails/felinid/cat/tail = /datum/sprite_accessory/tails/felinid/cat
return initial(tail.name)
/datum/preference/choiced/felinid_ears
/datum/preference/choiced/species_feature/felinid_ears
savefile_key = "feature_human_ears" //savefile keys cannot be changed, blame whoever named them this way.
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
can_randomize = FALSE
relevant_organ = /obj/item/organ/ears/cat
/datum/preference/choiced/felinid_ears/init_possible_values()
return assoc_to_keys_features(SSaccessories.ears_list)
/datum/preference/choiced/felinid_ears/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_EARS] = value
/datum/preference/choiced/felinid_ears/create_default_value()
return /datum/sprite_accessory/ears/cat::name

View File

@@ -23,7 +23,7 @@
return final_icon
/datum/preference/choiced/lizard_body_markings
/datum/preference/choiced/species_feature/lizard_body_markings
savefile_key = "feature_lizard_body_markings"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -31,11 +31,8 @@
should_generate_icons = TRUE
relevant_body_markings = /datum/bodypart_overlay/simple/body_marking/lizard
/datum/preference/choiced/lizard_body_markings/init_possible_values()
return assoc_to_keys_features(SSaccessories.lizard_markings_list)
/datum/preference/choiced/lizard_body_markings/icon_for(value)
var/datum/sprite_accessory/sprite_accessory = SSaccessories.lizard_markings_list[value]
/datum/preference/choiced/species_feature/lizard_body_markings/icon_for(value)
var/datum/sprite_accessory/sprite_accessory = get_accessory_for_value(value)
var/datum/universal_icon/final_icon = uni_icon('icons/mob/human/species/lizard/bodyparts.dmi', "lizard_chest_m")
@@ -54,10 +51,7 @@
return final_icon
/datum/preference/choiced/lizard_body_markings/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_LIZARD_MARKINGS] = value
/datum/preference/choiced/lizard_frills
/datum/preference/choiced/species_feature/lizard_frills
savefile_key = "feature_lizard_frills"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -65,16 +59,10 @@
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/frills
/datum/preference/choiced/lizard_frills/init_possible_values()
return assoc_to_keys_features(SSaccessories.frills_list)
/datum/preference/choiced/species_feature/lizard_frills/icon_for(value)
return generate_lizard_side_shot(get_accessory_for_value(value), "frills")
/datum/preference/choiced/lizard_frills/icon_for(value)
return generate_lizard_side_shot(SSaccessories.frills_list[value], "frills")
/datum/preference/choiced/lizard_frills/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_FRILLS] = value
/datum/preference/choiced/lizard_horns
/datum/preference/choiced/species_feature/lizard_horns
savefile_key = "feature_lizard_horns"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -82,14 +70,8 @@
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/horns
/datum/preference/choiced/lizard_horns/init_possible_values()
return assoc_to_keys_features(SSaccessories.horns_list)
/datum/preference/choiced/lizard_horns/icon_for(value)
return generate_lizard_side_shot(SSaccessories.horns_list[value], "horns")
/datum/preference/choiced/lizard_horns/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_HORNS] = value
/datum/preference/choiced/species_feature/lizard_horns/icon_for(value)
return generate_lizard_side_shot(get_accessory_for_value(value), "horns")
/datum/preference/choiced/lizard_legs
savefile_key = "feature_lizard_legs"
@@ -130,7 +112,7 @@
var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species)
return initial(species_type.digitigrade_customization) == DIGITIGRADE_OPTIONAL
/datum/preference/choiced/lizard_snout
/datum/preference/choiced/species_feature/lizard_snout
savefile_key = "feature_lizard_snout"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -138,38 +120,17 @@
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/snout
/datum/preference/choiced/lizard_snout/init_possible_values()
return assoc_to_keys_features(SSaccessories.snouts_list)
/datum/preference/choiced/species_feature/lizard_snout/icon_for(value)
return generate_lizard_side_shot(get_accessory_for_value(value), "snout", include_snout = FALSE)
/datum/preference/choiced/lizard_snout/icon_for(value)
return generate_lizard_side_shot(SSaccessories.snouts_list[value], "snout", include_snout = FALSE)
/datum/preference/choiced/lizard_snout/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_SNOUT] = value
/datum/preference/choiced/lizard_spines
/datum/preference/choiced/species_feature/lizard_spines
savefile_key = "feature_lizard_spines"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
relevant_organ = /obj/item/organ/spines
/datum/preference/choiced/lizard_spines/init_possible_values()
return assoc_to_keys_features(SSaccessories.spines_list)
/datum/preference/choiced/lizard_spines/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_SPINES] = value
/datum/preference/choiced/lizard_tail
/datum/preference/choiced/species_feature/lizard_tail
savefile_key = "feature_lizard_tail"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
relevant_organ = /obj/item/organ/tail/lizard
/datum/preference/choiced/lizard_tail/init_possible_values()
return assoc_to_keys_features(SSaccessories.tails_list_lizard)
/datum/preference/choiced/lizard_tail/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_TAIL_LIZARD] = value
/datum/preference/choiced/lizard_tail/create_default_value()
return /datum/sprite_accessory/tails/lizard/smooth::name

View File

@@ -1,15 +1,6 @@
/datum/preference/choiced/monkey_tail
/datum/preference/choiced/species_feature/monkey_tail
savefile_key = "feature_monkey_tail"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
relevant_organ = /obj/item/organ/tail/monkey
can_randomize = FALSE
/datum/preference/choiced/monkey_tail/init_possible_values()
return assoc_to_keys_features(SSaccessories.tails_list_monkey)
/datum/preference/choiced/monkey_tail/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_TAIL_MONKEY] = value
/datum/preference/choiced/monkey_tail/create_default_value()
return /datum/sprite_accessory/tails/monkey/default::name

View File

@@ -1,4 +1,4 @@
/datum/preference/choiced/moth_antennae
/datum/preference/choiced/species_feature/moth_antennae
savefile_key = "feature_moth_antennae"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -6,10 +6,7 @@
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/antennae
/datum/preference/choiced/moth_antennae/init_possible_values()
return assoc_to_keys_features(SSaccessories.moth_antennae_list)
/datum/preference/choiced/moth_antennae/icon_for(value)
/datum/preference/choiced/species_feature/moth_antennae/icon_for(value)
var/static/datum/universal_icon/moth_head
if (isnull(moth_head))
@@ -17,7 +14,7 @@
moth_head.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_l"), ICON_OVERLAY)
moth_head.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_r"), ICON_OVERLAY)
var/datum/sprite_accessory/antennae = SSaccessories.moth_antennae_list[value]
var/datum/sprite_accessory/antennae = get_accessory_for_value(value)
var/datum/universal_icon/icon_with_antennae = moth_head.copy()
icon_with_antennae.blend_icon(uni_icon(antennae.icon, "m_moth_antennae_[antennae.icon_state]_FRONT"), ICON_OVERLAY)
@@ -26,10 +23,7 @@
return icon_with_antennae
/datum/preference/choiced/moth_antennae/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_MOTH_ANTENNAE] = value
/datum/preference/choiced/moth_markings
/datum/preference/choiced/species_feature/moth_markings
savefile_key = "feature_moth_markings"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -37,10 +31,7 @@
should_generate_icons = TRUE
relevant_body_markings = /datum/bodypart_overlay/simple/body_marking/moth
/datum/preference/choiced/moth_markings/init_possible_values()
return assoc_to_keys_features(SSaccessories.moth_markings_list)
/datum/preference/choiced/moth_markings/icon_for(value)
/datum/preference/choiced/species_feature/moth_markings/icon_for(value)
var/static/list/body_parts = list(
/obj/item/bodypart/head/moth,
/obj/item/bodypart/chest/moth,
@@ -58,7 +49,7 @@
moth_body.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_l"), ICON_OVERLAY)
moth_body.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_r"), ICON_OVERLAY)
var/datum/sprite_accessory/markings = SSaccessories.moth_markings_list[value]
var/datum/sprite_accessory/markings = get_accessory_for_value(value)
var/datum/universal_icon/icon_with_markings = moth_body.copy()
if (value != SPRITE_ACCESSORY_NONE)
@@ -76,10 +67,7 @@
return icon_with_markings
/datum/preference/choiced/moth_markings/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_MOTH_MARKINGS] = value
/datum/preference/choiced/moth_wings
/datum/preference/choiced/species_feature/moth_wings
savefile_key = "feature_moth_wings"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -87,12 +75,6 @@
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/wings/moth
/datum/preference/choiced/moth_wings/init_possible_values()
return assoc_to_keys_features(SSaccessories.moth_wings_list)
/datum/preference/choiced/moth_wings/icon_for(value)
var/datum/sprite_accessory/moth_wings = SSaccessories.moth_wings_list[value]
/datum/preference/choiced/species_feature/moth_wings/icon_for(value)
var/datum/sprite_accessory/moth_wings = get_accessory_for_value(value)
return uni_icon(moth_wings.icon, "m_moth_wings_[moth_wings.icon_state]_BEHIND")
/datum/preference/choiced/moth_wings/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_MOTH_WINGS] = value

View File

@@ -1,11 +1,5 @@
/datum/preference/choiced/mushroom_cap
/datum/preference/choiced/species_feature/mushroom_cap
savefile_key = "feature_mushperson_cap"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
relevant_organ = /obj/item/organ/mushroom_cap
/datum/preference/choiced/mushroom_cap/init_possible_values()
return assoc_to_keys_features(SSaccessories.caps_list)
/datum/preference/choiced/mushroom_cap/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_MUSH_CAP] = value

View File

@@ -1,4 +1,4 @@
/datum/preference/choiced/pod_hair
/datum/preference/choiced/species_feature/pod_hair
savefile_key = "feature_pod_hair"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -6,11 +6,8 @@
should_generate_icons = TRUE
relevant_organ = /obj/item/organ/pod_hair
/datum/preference/choiced/pod_hair/init_possible_values()
return assoc_to_keys_features(SSaccessories.pod_hair_list)
/datum/preference/choiced/pod_hair/icon_for(value)
var/datum/sprite_accessory/pod_hair = SSaccessories.pod_hair_list[value]
/datum/preference/choiced/species_feature/pod_hair/icon_for(value)
var/datum/sprite_accessory/pod_hair = get_accessory_for_value(value)
var/datum/universal_icon/icon_with_hair = uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "pod_head_m")
@@ -23,9 +20,3 @@
icon_with_hair.blend_color(COLOR_GREEN, ICON_MULTIPLY)
return icon_with_hair
/datum/preference/choiced/pod_hair/create_default_value()
return pick(assoc_to_keys_features(SSaccessories.pod_hair_list))
/datum/preference/choiced/pod_hair/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features[FEATURE_POD_HAIR] = value

View File

@@ -309,7 +309,8 @@
/datum/customer_data/moth/proc/get_wings(mob/living/basic/robot_customer/customer)
var/customer_ref = WEAKREF(customer)
if (!LAZYACCESS(wings_chosen, customer_ref))
LAZYSET(wings_chosen, customer_ref, SSaccessories.moth_wings_list[pick(SSaccessories.moth_wings_list)])
var/picked_wings = pick(SSaccessories.feature_list[FEATURE_MOTH_WINGS])
LAZYSET(wings_chosen, customer_ref, SSaccessories.feature_list[FEATURE_MOTH_WINGS][picked_wings])
return wings_chosen[customer_ref]
/datum/customer_data/moth/get_underlays(mob/living/basic/robot_customer/customer)

View File

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

View File

@@ -219,8 +219,7 @@
var/list/radial_wings = list()
var/list/name2type = list()
for(var/obj/item/organ/wings/functional/possible_type as anything in wing_types)
var/datum/sprite_accessory/accessory = initial(possible_type.sprite_accessory_override) //get the type
accessory = SSaccessories.wings_list[initial(accessory.name)] //get the singleton instance
var/datum/sprite_accessory/accessory = SSaccessories.feature_list[FEATURE_WINGS][possible_type::sprite_accessory_override::name] //get the singleton instance
var/image/img = image(icon = accessory.icon, icon_state = "m_wingsopen_[accessory.icon_state]_BEHIND") //Process the HUD elements
img.transform *= 0.5
img.pixel_w = -32

View File

@@ -113,21 +113,8 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/proc/create_consistent_human_dna(mob/living/carbon/human/target)
target.dna.features[FEATURE_MUTANT_COLOR] = COLOR_VIBRANT_LIME
target.dna.features[FEATURE_ETHEREAL_COLOR] = COLOR_WHITE
target.dna.features[FEATURE_LIZARD_MARKINGS] = get_consistent_feature_entry(SSaccessories.lizard_markings_list)
target.dna.features[FEATURE_EARS] = get_consistent_feature_entry(SSaccessories.ears_list)
target.dna.features[FEATURE_FRILLS] = get_consistent_feature_entry(SSaccessories.frills_list)
target.dna.features[FEATURE_HORNS] = get_consistent_feature_entry(SSaccessories.horns_list)
target.dna.features[FEATURE_MOTH_ANTENNAE] = get_consistent_feature_entry(SSaccessories.moth_antennae_list)
target.dna.features[FEATURE_MOTH_MARKINGS] = get_consistent_feature_entry(SSaccessories.moth_markings_list)
target.dna.features[FEATURE_MOTH_WINGS] = get_consistent_feature_entry(SSaccessories.moth_wings_list)
target.dna.features[FEATURE_SNOUT] = get_consistent_feature_entry(SSaccessories.snouts_list)
target.dna.features[FEATURE_SPINES] = get_consistent_feature_entry(SSaccessories.spines_list)
target.dna.features[FEATURE_TAIL_CAT] = get_consistent_feature_entry(SSaccessories.tails_list_felinid) // it's a lie
target.dna.features[FEATURE_TAIL_LIZARD] = get_consistent_feature_entry(SSaccessories.tails_list_lizard)
target.dna.features[FEATURE_TAIL_MONKEY] = get_consistent_feature_entry(SSaccessories.tails_list_monkey)
target.dna.features[FEATURE_TAIL_FISH] = get_consistent_feature_entry(SSaccessories.tails_list_fish)
target.dna.features[FEATURE_POD_HAIR] = get_consistent_feature_entry(SSaccessories.pod_hair_list)
target.dna.features[FEATURE_MUSH_CAP] = get_consistent_feature_entry(SSaccessories.caps_list)
for(var/feature_key in SSaccessories.feature_list)
target.dna.features[feature_key] = get_consistent_feature_entry(SSaccessories.feature_list[feature_key])
target.dna.initialize_dna(newblood_type = get_blood_type(BLOOD_TYPE_O_MINUS), create_mutation_blocks = FALSE, randomize_features = FALSE)
// UF and UI are nondeterministic, even though the features are the same some blocks will randomize slightly
// In practice this doesn't matter, but this is for the sake of 100%(ish) consistency

View File

@@ -28,9 +28,9 @@
/datum/species/human/felinid/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load, regenerate_icons = TRUE, replace_missing = TRUE)
if(!pref_load) //Hah! They got forcefully purrbation'd. Force default felinid parts on them if they have no mutant parts in those areas!
if(human_who_gained_species.dna.features[FEATURE_TAIL_CAT] == SPRITE_ACCESSORY_NONE)
human_who_gained_species.dna.features[FEATURE_TAIL_CAT] = get_consistent_feature_entry(SSaccessories.tails_list_felinid)
human_who_gained_species.dna.features[FEATURE_TAIL_CAT] = get_consistent_feature_entry(SSaccessories.feature_list[FEATURE_TAIL_CAT])
if(human_who_gained_species.dna.features[FEATURE_EARS] == SPRITE_ACCESSORY_NONE)
human_who_gained_species.dna.features[FEATURE_EARS] = get_consistent_feature_entry(SSaccessories.ears_list)
human_who_gained_species.dna.features[FEATURE_EARS] = get_consistent_feature_entry(SSaccessories.feature_list[FEATURE_EARS])
// Swapping out feline ears for normal ol' human ears if they have invisible cat ears.
if(human_who_gained_species.dna.features[FEATURE_EARS] == SPRITE_ACCESSORY_NONE)

View File

@@ -52,7 +52,7 @@
/datum/species/lizard/randomize_features()
var/list/features = ..()
features[FEATURE_LIZARD_MARKINGS] = pick(SSaccessories.lizard_markings_list)
features[FEATURE_LIZARD_MARKINGS] = pick(SSaccessories.feature_list[FEATURE_LIZARD_MARKINGS])
return features
/datum/species/lizard/get_scream_sound(mob/living/carbon/human/lizard)

View File

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

View File

@@ -59,7 +59,7 @@
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_EXTERNAL_POD_HAIR
dna_block = /datum/dna_block/feature/mush_cap
dna_block = /datum/dna_block/feature/accessory/mush_cap
restyle_flags = EXTERNAL_RESTYLE_PLANT
bodypart_overlay = /datum/bodypart_overlay/mutant/mushroom_cap
@@ -72,9 +72,6 @@
feature_key = FEATURE_MUSH_CAP
dyable = TRUE
/datum/bodypart_overlay/mutant/mushroom_cap/get_global_feature_list()
return SSaccessories.caps_list
/datum/bodypart_overlay/mutant/mushroom_cap/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEHAIR)

View File

@@ -23,7 +23,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
var/restyle_flags = NONE
///If not null, overrides the appearance with this sprite accessory datum
var/sprite_accessory_override
var/datum/sprite_accessory/sprite_accessory_override
/**accessory_type is optional if you haven't set sprite_datums for the object, and is used mostly to generate sprite_datums from a persons DNA
* For _mob_sprite we make a distinction between "Round Snout" and "round". Round Snout is the name of the sprite datum, while "round" would be part of the sprite
@@ -32,7 +32,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/obj/item/organ/proc/setup_bodypart_overlay(accessory_type)
bodypart_overlay = new bodypart_overlay(src)
accessory_type = accessory_type ? accessory_type : sprite_accessory_override
accessory_type ||= sprite_accessory_override
var/update_overlays = TRUE
if(accessory_type)
bodypart_overlay.set_appearance(accessory_type)
@@ -107,7 +107,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_EXTERNAL_HORNS
dna_block = /datum/dna_block/feature/horn
dna_block = /datum/dna_block/feature/accessory/horn
restyle_flags = EXTERNAL_RESTYLE_ENAMEL
bodypart_overlay = /datum/bodypart_overlay/mutant/horns
@@ -122,9 +122,6 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/datum/bodypart_overlay/mutant/horns/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEHAIR)
/datum/bodypart_overlay/mutant/horns/get_global_feature_list()
return SSaccessories.horns_list
///The frills of a lizard (like weird fin ears)
/obj/item/organ/frills
name = "frills"
@@ -134,7 +131,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_EXTERNAL_FRILLS
dna_block = /datum/dna_block/feature/frill
dna_block = /datum/dna_block/feature/accessory/frill
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/frills
@@ -148,9 +145,6 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/datum/bodypart_overlay/mutant/frills/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEEARS)
/datum/bodypart_overlay/mutant/frills/get_global_feature_list()
return SSaccessories.frills_list
///Guess what part of the lizard this is?
/obj/item/organ/snout
name = "lizard snout"
@@ -162,7 +156,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
external_bodyshapes = BODYSHAPE_SNOUTED
dna_block = /datum/dna_block/feature/snout
dna_block = /datum/dna_block/feature/accessory/snout
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/snout
@@ -176,9 +170,6 @@ Unlike normal organs, we're actually inside a persons limbs at all times
/datum/bodypart_overlay/mutant/snout/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDESNOUT)
/datum/bodypart_overlay/mutant/snout/get_global_feature_list()
return SSaccessories.snouts_list
///A moth's antennae
/obj/item/organ/antennae
name = "moth antennae"
@@ -188,7 +179,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_EXTERNAL_ANTENNAE
dna_block = /datum/dna_block/feature/moth_antenna
dna_block = /datum/dna_block/feature/accessory/moth_antenna
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/antennae
@@ -254,9 +245,6 @@ Unlike normal organs, we're actually inside a persons limbs at all times
burn_datum = fetch_sprite_datum(burn_datum) //turn the path into the singleton instance
/datum/bodypart_overlay/mutant/antennae/get_global_feature_list()
return SSaccessories.moth_antennae_list
/datum/bodypart_overlay/mutant/antennae/get_base_icon_state()
return burnt ? burn_datum.icon_state : sprite_datum.icon_state
@@ -273,7 +261,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times
use_mob_sprite_as_obj_sprite = TRUE
dna_block = /datum/dna_block/feature/pod_hair
dna_block = /datum/dna_block/feature/accessory/pod_hair
restyle_flags = EXTERNAL_RESTYLE_PLANT
bodypart_overlay = /datum/bodypart_overlay/mutant/pod_hair
@@ -291,9 +279,6 @@ Unlike normal organs, we're actually inside a persons limbs at all times
///The individual rgb colors are subtracted from this to get the color shifted layer
var/color_inverse_base = 255
/datum/bodypart_overlay/mutant/pod_hair/get_global_feature_list()
return SSaccessories.pod_hair_list
/datum/bodypart_overlay/mutant/pod_hair/color_image(image/overlay, draw_layer, obj/item/bodypart/limb)
if(draw_layer != bitflag_to_layer(color_swapped_layer))
return ..()

View File

@@ -7,7 +7,7 @@
zone = BODY_ZONE_CHEST
slot = ORGAN_SLOT_EXTERNAL_SPINES
dna_block = /datum/dna_block/feature/spine
dna_block = /datum/dna_block/feature/accessory/spine
restyle_flags = EXTERNAL_RESTYLE_FLESH
bodypart_overlay = /datum/bodypart_overlay/mutant/spines
@@ -32,9 +32,6 @@
feature_key = FEATURE_SPINES
dyable = TRUE
/datum/bodypart_overlay/mutant/spines/get_global_feature_list()
return SSaccessories.spines_list
/datum/bodypart_overlay/mutant/spines/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEJUMPSUIT)

View File

@@ -7,7 +7,7 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = ORGAN_SLOT_EXTERNAL_TAIL
dna_block = /datum/dna_block/feature/tail
dna_block = /datum/dna_block/feature/accessory/tail
restyle_flags = EXTERNAL_RESTYLE_FLESH
// defaults to cat, but the parent type shouldn't be created regardless
@@ -166,9 +166,6 @@
feature_key = FEATURE_TAIL_CAT
color_source = ORGAN_COLOR_HAIR
/datum/bodypart_overlay/mutant/tail/cat/get_global_feature_list()
return SSaccessories.tails_list_felinid
/obj/item/organ/tail/monkey
name = "monkey tail"
icon_state = "severedmonkeytail"
@@ -180,9 +177,6 @@
color_source = NONE
feature_key = FEATURE_TAIL_MONKEY
/datum/bodypart_overlay/mutant/tail/monkey/get_global_feature_list()
return SSaccessories.tails_list_monkey
/obj/item/organ/tail/xeno
name = "alien tail"
desc = "A long and flexible tail slightly resembling a spine, used by its original owner as both weapon and balance aid."
@@ -230,9 +224,6 @@
. = ..()
set_appearance_from_name(default_appearance)
/datum/bodypart_overlay/mutant/tail/xeno/get_global_feature_list()
return SSaccessories.tails_list_xeno
/datum/bodypart_overlay/mutant/tail/xeno/randomize_appearance()
set_appearance_from_name(default_appearance)
@@ -246,15 +237,12 @@
bodypart_overlay = /datum/bodypart_overlay/mutant/tail/lizard
wag_flags = WAG_ABLE
dna_block = /datum/dna_block/feature/tail_lizard
dna_block = /datum/dna_block/feature/accessory/tail_lizard
///Lizard tail bodypart overlay datum
/datum/bodypart_overlay/mutant/tail/lizard
feature_key = FEATURE_TAIL_LIZARD
/datum/bodypart_overlay/mutant/tail/lizard/get_global_feature_list()
return SSaccessories.tails_list_lizard
/obj/item/organ/tail/lizard/fake
name = "fabricated lizard tail"
desc = "A fabricated severed lizard tail. This one's made of synthflesh. Probably not usable for lizard wine."
@@ -268,9 +256,6 @@
/// Key for tail spine states, depends on the shape of the tail. Defined in the tail sprite datum.
var/tail_spine_key = NONE
/datum/bodypart_overlay/mutant/tail_spines/get_global_feature_list()
return SSaccessories.tail_spines_list
/datum/bodypart_overlay/mutant/tail_spines/get_base_icon_state()
return (!isnull(tail_spine_key) ? "[tail_spine_key]_" : "") + (wagging ? "wagging_" : "") + sprite_datum.icon_state // Select the wagging state if appropriate

View File

@@ -166,19 +166,16 @@
/datum/bodypart_overlay/mutant/wings/functional
///Are our wings currently open? Change through open_wings or close_wings()
VAR_PRIVATE/wings_open = FALSE
///Feature render key for opened wings
var/open_feature_key = "wingsopen"
/datum/bodypart_overlay/mutant/wings/functional/get_global_feature_list()
if(wings_open)
return SSaccessories.wings_open_list
else
return SSaccessories.wings_list
return SSaccessories.feature_list[FEATURE_WINGS_OPEN]
return ..()
///Update our wingsprite to the open wings variant
/datum/bodypart_overlay/mutant/wings/functional/proc/open_wings()
wings_open = TRUE
feature_key = open_feature_key
feature_key = FEATURE_WINGS_OPEN
set_appearance_from_name(sprite_datum.name) //It'll look for the same name again, but this time from the open wings list
///Update our wingsprite to the closed wings variant

View File

@@ -5,7 +5,7 @@
name = "moth wings"
desc = "Spread your wings and FLOOOOAAAAAT!"
dna_block = /datum/dna_block/feature/moth_wing
dna_block = /datum/dna_block/feature/accessory/moth_wing
bodypart_overlay = /datum/bodypart_overlay/mutant/wings/moth
restyle_flags = EXTERNAL_RESTYLE_FLESH
@@ -107,9 +107,6 @@
burn_datum = fetch_sprite_datum(burn_datum)
/datum/bodypart_overlay/mutant/wings/moth/get_global_feature_list()
return SSaccessories.moth_wings_list
/datum/bodypart_overlay/mutant/wings/moth/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEMUTWINGS)

View File

@@ -147,7 +147,7 @@
restyle_flags = EXTERNAL_RESTYLE_FLESH
dna_block = /datum/dna_block/feature/ears
dna_block = /datum/dna_block/feature/accessory/ears
bodypart_overlay = /datum/bodypart_overlay/mutant/cat_ears
@@ -161,9 +161,6 @@
/// Layer upon which we add the inner ears overlay
var/inner_layer = EXTERNAL_FRONT
/datum/bodypart_overlay/mutant/cat_ears/get_global_feature_list()
return SSaccessories.ears_list
/datum/bodypart_overlay/mutant/cat_ears/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner)
return !(bodypart_owner.owner?.obscured_slots & HIDEHAIR)