From f69a9634fa9221a6fdde7699c02fea8861748131 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sun, 21 Jun 2026 20:25:01 -0500 Subject: [PATCH] Standing overlay cleanup (#96593) ## About The Pull Request Every standing overlay layer that wasn't actually a standing overlay layer is now a "sub-standing layer", denoted with a decimal Total number of standing overlay layers reduced from 39 to 23 Small refactors to mutation overlay/species handling and cult halo. ## Why It's Good For The Game Makes it easier to work with mob overlays. You can see at a glance which layers are applied with standing overlays and which layers are manually handled, and more importantly you can see how the layers sort themselves out by just looking at them. Very convenient. ## Changelog :cl: Melbert code: Reorganized human layering, report any oddities with sprites, such as mislayered equipment. refactor: Refactored cult halos, report any oddities like them not appearing when they should. refactor: Refactored mutation visuals, they're now affected by height! Report any oddities like them disappearing. refactor: Refactored mutation species handling, report any oddities like lizards with hulk. /:cl: --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/__DEFINES/mobs.dm | 218 ++++++++---------- code/_globalvars/layers.dm | 27 ++- code/datums/dna/dna.dm | 8 +- code/datums/elements/cult_halo.dm | 53 ----- code/datums/mutations/_mutations.dm | 67 +++--- code/datums/mutations/adaptation.dm | 18 +- code/datums/mutations/antenna.dm | 20 +- code/datums/mutations/radioactive.dm | 9 +- code/datums/mutations/reach.dm | 9 +- code/datums/mutations/sight.dm | 9 +- .../proximity_monitor/fields/heretic_arena.dm | 10 + code/datums/status_effects/buffs/cult_halo.dm | 52 +++++ code/game/objects/effects/decals/crayon.dm | 2 +- code/game/objects/structures/mirror.dm | 2 - .../antagonists/cult/datums/cult_team.dm | 2 +- .../antagonists/cult/datums/cultist.dm | 5 +- .../modules/deathmatch/deathmatch_loadouts.dm | 2 +- .../living/carbon/human/human_update_icons.dm | 1 - tgstation.dme | 2 +- 19 files changed, 227 insertions(+), 289 deletions(-) delete mode 100644 code/datums/elements/cult_halo.dm create mode 100644 code/datums/status_effects/buffs/cult_halo.dm diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index fc6113046ca..3f3d01a6db2 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -765,87 +765,109 @@ GLOBAL_LIST_INIT(human_heights_to_offsets, list( "[HUMAN_HEIGHT_TALLEST]" = list(3, 2), )) -// Mob Overlays Indexes -/// Total number of layers for mob overlays -/// KEEP THIS UP-TO-DATE OR SHIT WILL BREAK -/// Also consider updating layers_to_offset -#define TOTAL_LAYERS 39 -/// Mutations layer - Tk headglows, cold resistance glow, etc -#define MUTATIONS_LAYER 38 -/// Mutantrace features (tail when looking south) that must appear behind the body parts -#define BODY_BEHIND_LAYER 37 -/// Layer for bodyparts that should appear behind every other bodypart - Mostly, legs when facing WEST or EAST -#define BODYPARTS_LOW_LAYER 36 -/// Layer for most bodyparts, appears above BODYPARTS_LOW_LAYER and below BODYPARTS_HIGH_LAYER -#define BODYPARTS_LAYER 35 -/// Mutantrace features (snout, body markings) that must appear above the body parts -#define BODY_ADJ_LAYER 34 -/// Underwear, undershirts, socks -#define BODY_LAYER 33 -/// Eyes and eyelids -#define EYES_LAYER 32 -/// Mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) -#define FRONT_MUTATIONS_LAYER 31 -/// Damage indicators (cuts and burns) -#define DAMAGE_LAYER 30 -/// Jumpsuit clothing layer -#define UNIFORM_LAYER 29 -/// ID card layer -#define ID_LAYER 28 -/// ID card layer (might be deprecated) -#define ID_CARD_LAYER 27 -/// Layer for bodyparts that should appear above every other bodypart - Currently only used for hands -#define BODYPARTS_HIGH_LAYER 26 -/// Gloves layer -#define GLOVES_LAYER 25 -/// Shoes layer -#define SHOES_LAYER 24 -/// Layer for masks that are worn below ears and eyes (like Balaclavas) (layers below hair, use flagsinv=HIDEHAIR as needed) -#define LOW_FACEMASK_LAYER 23 -/// Ears layer (Spessmen have ears? Wow) -#define EARS_LAYER 22 -/// Layer for neck apperal that should appear below the suit slot (like neckties) -#define LOW_NECK_LAYER 21 -/// Suit layer (armor, coats, etc.) -#define SUIT_LAYER 20 -/// Glasses layer -#define GLASSES_LAYER 19 -/// Belt layer -#define BELT_LAYER 18 //Possible make this an overlay of something required to wear a belt? -/// Suit storage layer (tucking a gun or baton underneath your armor) -#define SUIT_STORE_LAYER 17 -/// Neck layer (for wearing capes and bedsheets) -#define NECK_LAYER 16 -/// Back layer (for backpacks and equipment on your back) -#define BACK_LAYER 15 -/// Special layer for rendering beneath hair, for special facemasks -#define BENEATH_HAIR_LAYER 14 -/// Hair layer (mess with the fro and you got to go!) -#define HAIR_LAYER 13 //TODO: make part of head layer? -/// Facemask layer (gas masks, breath masks, etc.) -#define FACEMASK_LAYER 12 -/// Head layer (hats, helmets, etc.) -#define HEAD_LAYER 11 -/// Hair that layers out above clothing, including hats (high ponytails and such) -#define OUTER_HAIR_LAYER 10 -/// Handcuff layer (when your hands are cuffed) -#define HANDCUFF_LAYER 9 -/// Legcuff layer (when your feet are cuffed) -#define LEGCUFF_LAYER 8 -/// Hands layer (for the actual hand, not the arm... I think?) -#define HANDS_LAYER 7 -/// Body front layer. Usually used for mutant bodyparts that need to be in front of stuff (e.g. cat ears) -#define BODY_FRONT_LAYER 6 -/// Special body layer that actually require to be above the hair (e.g. lifted welding goggles) -#define ABOVE_BODY_FRONT_GLASSES_LAYER 5 -/// Special body layer for the rare cases where something on the head needs to be above everything else (e.g. flowers) -#define ABOVE_BODY_FRONT_HEAD_LAYER 4 -/// Bleeding wound icons -#define WOUND_LAYER 3 -/// Blood cult ascended halo layer, because there's currently no better solution for adding/removing -#define HALO_LAYER 2 +/* + * Mob overlays + * + * Integers are standing overlays which are managed by the mob overlay system + * They're drawn in order from least to greatest, with the lowest layer drawn highest on the screen + * + * Decimals are sub-layers or alternate layers + * These are not managed by the mob overlay system, but still layer according to their value + * + * Please keep this organized and in order, to make it easier to see at a glance how mobs layer + * Don't be afraid to change the values of existing layers either, it won't break anything so long as order is the same + * + * If you want to add a new layer, make sure to update TOTAL_LAYERS and layers_to_offset as needed + * NOTE: You ONLY need to add standing layers if you're using the standing overlay system, ie. apply_overlay(YOUR_LAYER)! + * If you're NOT using this sytem just add it as a sub-layer where appropriate + */ + /// The highest most layer for mob overlays. Unused #define HIGHEST_LAYER 1 + /// Blood cult ascended halo layer + #define HALO_LAYER 1.1 +/// Bleeding wound icons +#define WOUND_LAYER 2 + /// Special body layer for the rare cases where something on the head needs to be above everything else (e.g. flowers) + #define ABOVE_BODY_FRONT_HEAD_LAYER 2.3 + /// The layer above mutant body parts + #define ABOVE_BODY_FRONT_LAYER 2.4 + /// Body front layer. Usually used for mutant bodyparts that need to be in front of stuff (e.g. cat ears) + #define BODY_FRONT_LAYER 2.5 +/// Hands layer (for the actual hand, not the arm... I think?) +#define HANDS_LAYER 3 +/// Legcuff layer (when your feet are cuffed) +#define LEGCUFF_LAYER 4 +/// Handcuff layer (when your hands are cuffed) +#define HANDCUFF_LAYER 5 + /// Hair that layers out above clothing, including hats (high ponytails and such) + #define OUTER_HAIR_LAYER 5.9 +/// Head layer (hats, helmets, etc.) +#define HEAD_LAYER 6 + /// The layer underneath the head (for hats) + #define UNDER_HEAD_LAYER 6.1 +/// Facemask layer (gas masks, breath masks, etc.) +#define FACEMASK_LAYER 7 +/// Hair layer (mess with the fro and you got to go!) +#define HAIR_LAYER 8 + /// Special layer for rendering beneath hair, for special facemasks + #define BENEATH_HAIR_LAYER 8.1 +/// Back layer (for backpacks and equipment on your back) +#define BACK_LAYER 9 +/// Neck layer (for wearing capes and bedsheets) +#define NECK_LAYER 10 +/// Suit storage layer (tucking a gun or baton underneath your armor) +#define SUIT_STORE_LAYER 11 +/// Belt layer +#define BELT_LAYER 12 //Possible make this an overlay of something required to wear a belt? +/// Glasses layer +#define GLASSES_LAYER 13 +/// Suit layer (armor, coats, etc.) +#define SUIT_LAYER 14 + /// The layer underneath the suit + #define UNDER_SUIT_LAYER 14.1 + /// Layer for neck apperal that should appear below the suit slot (like neckties) + #define LOW_NECK_LAYER 14.2 +/// Ears layer (Spessmen have ears? Wow) +#define EARS_LAYER 15 + /// Layer for masks that are worn below ears and eyes (like Balaclavas) (layers below hair, use flagsinv=HIDEHAIR as needed) + #define LOW_FACEMASK_LAYER 15.1 + /// The layer above shoes + #define ABOVE_SHOES_LAYER 15.9 +/// Shoes layer +#define SHOES_LAYER 16 +/// Gloves layer +#define GLOVES_LAYER 17 + /// Layer for bodyparts that should appear above every other bodypart - Currently only used for hands + #define BODYPARTS_HIGH_LAYER 17.5 +/// ID card layer +#define ID_LAYER 18 +/// Jumpsuit clothing layer +#define UNIFORM_LAYER 19 + /// The layer underneath the uniform + #define UNDER_UNIFORM_LAYER 19.1 +/// Damage indicators (cuts and burns) +#define DAMAGE_LAYER 20 + /// Mutations that should appear above everything else (e.g. laser eyes) + #define FRONT_MUTATIONS_LAYER 20.9 +/// Eyes and eyelids +#define EYES_LAYER 21 +/// Underwear, undershirts, socks +#define BODY_LAYER 22 + /// Mutantrace features (snout, body markings) that must appear above the body parts + #define BODY_ADJ_LAYER 22.9 +/// Layer for most bodyparts, appears above BODYPARTS_LOW_LAYER and below BODYPARTS_HIGH_LAYER +#define BODYPARTS_LAYER 23 + /// Layer for bodyparts that should appear behind every other bodypart - Mostly, legs when facing WEST or EAST + #define BODYPARTS_LOW_LAYER 23.1 + /// Mutantrace features (tail when looking south) that must appear behind the body parts + #define BODY_BEHIND_LAYER 23.2 + /// Mutations layer - Tk headglows, cold resistance glow, etc. Very bottom of the mob + #define MUTATIONS_LAYER 23.9 +/// Total number of standing overlays. +/// KEEP THIS UP-TO-DATE OR SHIT WILL BREAK. +/// (You ONLY need to update this if you add a standing overlay, adding an integer.) +#define TOTAL_LAYERS 23 #define UPPER_BODY "upper body" #define LOWER_BODY "lower body" @@ -869,32 +891,10 @@ GLOBAL_LIST_INIT(layers_to_offset, list( // (In practice they'd be fine if they got a filter but we can optimize a bit by not.) "[NECK_LAYER]" = UPPER_BODY, "[GLASSES_LAYER]" = UPPER_BODY, - "[LOW_NECK_LAYER]" = UPPER_BODY, - "[ABOVE_BODY_FRONT_GLASSES_LAYER]" = UPPER_BODY, // currently unused - "[ABOVE_BODY_FRONT_HEAD_LAYER]" = UPPER_BODY, // only used for head stuff "[GLOVES_LAYER]" = LOWER_BODY, - "[HALO_LAYER]" = UPPER_BODY, // above the head "[HANDCUFF_LAYER]" = LOWER_BODY, - "[ID_CARD_LAYER]" = UPPER_BODY, // unused "[ID_LAYER]" = UPPER_BODY, "[FACEMASK_LAYER]" = UPPER_BODY, - "[LOW_FACEMASK_LAYER]" = UPPER_BODY, - "[BENEATH_HAIR_LAYER]" = UPPER_BODY, // alt mask layer - // These two are cached, and have their appearance shared(?), so it's safer to just not touch it - "[MUTATIONS_LAYER]" = NO_MODIFY, - "[FRONT_MUTATIONS_LAYER]" = NO_MODIFY, - // These DO get a filter, I'm leaving them here as reference, - // to show how many filters are added at a glance - // BACK_LAYER (backpacks are big) - // BODYPARTS_HIGH_LAYER (arms) - // BODY_LAYER (body markings (full body), underwear (full body)) - // BODY_ADJ_LAYER (external organs like wings) - // BODY_BEHIND_LAYER (external organs like wings) - // BODY_FRONT_LAYER (external organs like wings) - // DAMAGE_LAYER (full body) - // HIGHEST_LAYER (full body) - // UNIFORM_LAYER (full body) - // WOUND_LAYER (full body) )) //Bitflags for the layers a bodypart overlay can draw on (can be drawn on multiple layers) @@ -916,22 +916,6 @@ GLOBAL_LIST_INIT(layers_to_offset, list( /// This organ allows restyling with enamel restyling (like a fucking file or something?). It's for horns and shit #define EXTERNAL_RESTYLE_ENAMEL (1 << 2) -//Mob Overlay Index Shortcuts for alternate_worn_layer, layers -//Because I *KNOW* somebody will think layer+1 means "above" -//IT DOESN'T OK, IT MEANS "UNDER" -/// The layer underneath the uniform -#define UNDER_UNIFORM_LAYER (UNIFORM_LAYER+1) -/// The layer underneath the suit -#define UNDER_SUIT_LAYER (SUIT_LAYER+1) -/// The layer underneath the head (for hats) -#define UNDER_HEAD_LAYER (HEAD_LAYER+1) - -//AND -1 MEANS "ABOVE", OK?, OK!?! -/// The layer above shoes -#define ABOVE_SHOES_LAYER (SHOES_LAYER-1) -/// The layer above mutant body parts -#define ABOVE_BODY_FRONT_LAYER (BODY_FRONT_LAYER-1) - /// If gravity must be present to perform action (can't use pens without gravity) #define NEED_GRAVITY (1<<0) /// If reading is required to perform action (can't read a book if you are illiterate) diff --git a/code/_globalvars/layers.dm b/code/_globalvars/layers.dm index 4652d00a389..8f1d01c4ab8 100644 --- a/code/_globalvars/layers.dm +++ b/code/_globalvars/layers.dm @@ -150,42 +150,45 @@ GLOBAL_LIST_INIT(admin_readable_layers, list( "LIGHTING_ABOVE_ALL" = LIGHTING_ABOVE_ALL, "EMISSIVE_LAYER_UNBLOCKABLE" = EMISSIVE_LAYER_UNBLOCKABLE, // Mob layers - "MUTATIONS_LAYER" = -MUTATIONS_LAYER, "BODY_BEHIND_LAYER" = -BODY_BEHIND_LAYER, - "BODYPARTS_LOW_LAYER" = -BODYPARTS_LOW_LAYER, "BODYPARTS_LAYER" = -BODYPARTS_LAYER, "BODY_ADJ_LAYER" = -BODY_ADJ_LAYER, "BODY_LAYER" = -BODY_LAYER, "EYES_LAYER" = -EYES_LAYER, - "FRONT_MUTATIONS_LAYER" = -FRONT_MUTATIONS_LAYER, "DAMAGE_LAYER" = -DAMAGE_LAYER, "UNIFORM_LAYER" = -UNIFORM_LAYER, "ID_LAYER" = -ID_LAYER, - "ID_CARD_LAYER" = -ID_CARD_LAYER, - "BODYPARTS_HIGH_LAYER" = -BODYPARTS_HIGH_LAYER, "GLOVES_LAYER" = -GLOVES_LAYER, - "UNDER_UNIFORM_LAYER" = -UNDER_UNIFORM_LAYER, "SHOES_LAYER" = -SHOES_LAYER, - "LOW_FACEMASK_LAYER" = -LOW_FACEMASK_LAYER, "EARS_LAYER" = -EARS_LAYER, - "LOW_NECK_LAYER" = -LOW_NECK_LAYER, "SUIT_LAYER" = -SUIT_LAYER, "GLASSES_LAYER" = -GLASSES_LAYER, "BELT_LAYER" = -BELT_LAYER, "SUIT_STORE_LAYER" = -SUIT_STORE_LAYER, "NECK_LAYER" = -NECK_LAYER, "BACK_LAYER" = -BACK_LAYER, - "BENEATH_HAIR_LAYER" = -BENEATH_HAIR_LAYER, "HAIR_LAYER" = -HAIR_LAYER, "FACEMASK_LAYER" = -FACEMASK_LAYER, "HEAD_LAYER" = -HEAD_LAYER, - "OUTER_HAIR_LAYER" = -OUTER_HAIR_LAYER, "HANDCUFF_LAYER" = -HANDCUFF_LAYER, "LEGCUFF_LAYER" = -LEGCUFF_LAYER, "HANDS_LAYER" = -HANDS_LAYER, "BODY_FRONT_LAYER" = -BODY_FRONT_LAYER, - "ABOVE_BODY_FRONT_GLASSES_LAYER" = -ABOVE_BODY_FRONT_GLASSES_LAYER, - "ABOVE_BODY_FRONT_HEAD_LAYER" = -ABOVE_BODY_FRONT_HEAD_LAYER, "WOUND_LAYER" = -WOUND_LAYER, + // Alt. mob layers + "ABOVE_BODY_FRONT_HEAD_LAYER" = -ABOVE_BODY_FRONT_HEAD_LAYER, + "ABOVE_BODY_FRONT_LAYER" = -ABOVE_BODY_FRONT_LAYER, + "ABOVE_SHOES_LAYER" = -ABOVE_SHOES_LAYER, + "BENEATH_HAIR_LAYER" = -BENEATH_HAIR_LAYER, + "BODYPARTS_HIGH_LAYER" = -BODYPARTS_HIGH_LAYER, + "BODYPARTS_LOW_LAYER" = -BODYPARTS_LOW_LAYER, + "FRONT_MUTATIONS_LAYER" = -FRONT_MUTATIONS_LAYER, "HALO_LAYER" = -HALO_LAYER, + "LOW_FACEMASK_LAYER" = -LOW_FACEMASK_LAYER, + "LOW_NECK_LAYER" = -LOW_NECK_LAYER, + "MUTATIONS_LAYER" = -MUTATIONS_LAYER, + "OUTER_HAIR_LAYER" = -OUTER_HAIR_LAYER, + "UNDER_HEAD_LAYER" = -UNDER_HEAD_LAYER, + "UNDER_SUIT_LAYER" = -UNDER_SUIT_LAYER, + "UNDER_UNIFORM_LAYER" = -UNDER_UNIFORM_LAYER, )) diff --git a/code/datums/dna/dna.dm b/code/datums/dna/dna.dm index effe0bd1ce5..5847b83a61e 100644 --- a/code/datums/dna/dna.dm +++ b/code/datums/dna/dna.dm @@ -410,10 +410,9 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) log_mob_tag("TAG: [tag] SPECIES: [key_name(src)] \[[mrace]\]") /mob/living/carbon/human/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE, replace_missing = TRUE) - ..() + . = ..() if(icon_update) update_body(is_creating = TRUE) - update_mutations_overlay()// no lizard with human hulk overlay please. /mob/proc/has_dna() return @@ -464,7 +463,6 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(mrace || newfeatures || unique_identity) update_body(is_creating = TRUE) - update_mutations_overlay() if(LAZYLEN(mutations) && force_transfer_mutations && can_mutate()) for(var/datum/mutation/mutation as anything in mutations) @@ -502,7 +500,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(icon_update) update_body(is_creating = mutcolor_update) if(mutations_overlay_update) - update_mutations_overlay() + update_appearance(UPDATE_OVERLAYS) /mob/proc/domutcheck() return @@ -514,7 +512,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) for(var/mutation in dna.mutation_index) dna.check_block(mutation) - update_mutations_overlay() + update_appearance(UPDATE_OVERLAYS) /datum/dna/proc/check_block(mutation_path) var/datum/mutation/mutation = get_mutation(mutation_path) diff --git a/code/datums/elements/cult_halo.dm b/code/datums/elements/cult_halo.dm deleted file mode 100644 index 684144ca0ab..00000000000 --- a/code/datums/elements/cult_halo.dm +++ /dev/null @@ -1,53 +0,0 @@ -/** - * # Cult halo element - * - * Applies and removes the cult halo - */ -/datum/element/cult_halo - -/datum/element/cult_halo/Attach(datum/target, initial_delay = 20 SECONDS) - . = ..() - if (!isliving(target)) - return ELEMENT_INCOMPATIBLE - - // Register signals for mob transformation to prevent premature halo removal - RegisterSignals(target, list(COMSIG_CHANGELING_TRANSFORM, COMSIG_MONKEY_HUMANIZE, COMSIG_HUMAN_MONKEYIZE), PROC_REF(set_halo)) - addtimer(CALLBACK(src, PROC_REF(set_halo), target), initial_delay) - -/** - * Halo setter proc - * - * Adds the cult halo overlays, and adds the halo trait to the mob. - */ -/datum/element/cult_halo/proc/set_halo(mob/living/target) - SIGNAL_HANDLER - - if(!IS_CULTIST(target)) - target.RemoveElement(/datum/element/cult_halo) - return - - ADD_TRAIT(target, TRAIT_CULT_HALO, CULT_TRAIT) - var/mutable_appearance/new_halo_overlay = mutable_appearance('icons/mob/effects/halo.dmi', "halo[rand(1, 6)]", -HALO_LAYER) - if (ishuman(target)) - var/mob/living/carbon/human/human_parent = target - new /obj/effect/temp_visual/cult/sparks(get_turf(human_parent), human_parent.dir) - human_parent.overlays_standing[HALO_LAYER] = new_halo_overlay - human_parent.apply_overlay(HALO_LAYER) - else - target.add_overlay(new_halo_overlay) - -/** - * Detach proc - * - * Removes the halo overlays, and trait from the mob - */ -/datum/element/cult_halo/Detach(mob/living/target, ...) - REMOVE_TRAIT(target, TRAIT_CULT_HALO, CULT_TRAIT) - if (ishuman(target)) - var/mob/living/carbon/human/human_parent = target - human_parent.remove_overlay(HALO_LAYER) - human_parent.update_body() - else - target.cut_overlay(HALO_LAYER) - UnregisterSignal(target, list(COMSIG_CHANGELING_TRANSFORM, COMSIG_HUMAN_MONKEYIZE, COMSIG_MONKEY_HUMANIZE)) - return ..() diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm index 7f88a696b5b..9616adfa742 100644 --- a/code/datums/mutations/_mutations.dm +++ b/code/datums/mutations/_mutations.dm @@ -30,12 +30,16 @@ var/text_gain_indication = "" /// Message given to the user upon losing this mutation var/text_lose_indication = "" - /// Visual indicators upon the character of the owner of this mutation - var/static/list/visual_indicators = list() /// The path of action we grant to our user on mutation gain var/datum/action/cooldown/power_path + /// Optional: Icon for overlay the mutation gives the mod + var/mutation_icon = 'icons/mob/effects/genetics.dmi' + /// Optional: Icon state for the overlay the mutation gives the mob + var/mutation_icon_state = "" /// Which mutation layer to use var/layer_used = MUTATIONS_LAYER + /// Where the mutation overlay is located + var/offset_location = UPPER_BODY /// To restrict mutation to only certain species var/list/species_allowed /// Minimum health required to acquire the mutation @@ -115,7 +119,7 @@ /datum/mutation/proc/on_acquiring(mob/living/carbon/human/acquirer) if(!acquirer || !istype(acquirer) || acquirer.stat == DEAD || (src in acquirer.dna.mutations)) return FALSE - if(species_allowed && !species_allowed.Find(acquirer.dna.species.id)) + if(LAZYLEN(species_allowed) && !LAZYFIND(species_allowed, acquirer.dna.species.id)) return FALSE if(health_req && acquirer.health < health_req) return FALSE @@ -132,14 +136,11 @@ SEND_SIGNAL(src, COMSIG_MUTATION_GAINED, acquirer) if(text_gain_indication) to_chat(owner, text_gain_indication) - if(visual_indicators.len) - var/list/mut_overlay = list(get_visual_indicator()) - if(owner.overlays_standing[layer_used]) - mut_overlay = owner.overlays_standing[layer_used] - mut_overlay |= get_visual_indicator() - owner.remove_overlay(layer_used) - owner.overlays_standing[layer_used] = mut_overlay - owner.apply_overlay(layer_used) + if(LAZYLEN(species_allowed)) + RegisterSignal(owner, COMSIG_SPECIES_GAIN, PROC_REF(on_species_change)) + if(mutation_icon && mutation_icon_state) + RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlay)) + owner.update_appearance(UPDATE_OVERLAYS) grant_power() //we do checks here so nothing about hulk getting magic if(mutation_traits) owner.add_traits(mutation_traits, GENETIC_MUTATION) @@ -156,41 +157,27 @@ return TRUE . = FALSE SEND_SIGNAL(src, COMSIG_MUTATION_LOST, owner) + UnregisterSignal(owner, list(COMSIG_SPECIES_GAIN, COMSIG_ATOM_UPDATE_OVERLAYS)) + if(mutation_icon && mutation_icon_state) + owner.update_appearance(UPDATE_OVERLAYS) if(text_lose_indication && owner.stat != DEAD) to_chat(owner, text_lose_indication) - if(visual_indicators.len) - var/list/mut_overlay = list() - if(owner.overlays_standing[layer_used]) - mut_overlay = owner.overlays_standing[layer_used] - owner.remove_overlay(layer_used) - mut_overlay.Remove(get_visual_indicator()) - owner.overlays_standing[layer_used] = mut_overlay - owner.apply_overlay(layer_used) - if(mutation_traits) owner.remove_traits(mutation_traits, GENETIC_MUTATION) -/mob/living/carbon/proc/update_mutations_overlay() - return +/datum/mutation/proc/on_species_change(datum/source, datum/species/new_species) + SIGNAL_HANDLER -/mob/living/carbon/human/update_mutations_overlay() - for(var/datum/mutation/mutation as anything in dna.mutations) - if(mutation.species_allowed && !mutation.species_allowed.Find(dna.species.id)) - dna.remove_mutation(mutation, mutation.sources) //shouldn't have that mutation at all - continue - if(mutation.visual_indicators.len == 0) - continue - var/list/mut_overlay = list() - if(overlays_standing[mutation.layer_used]) - mut_overlay = overlays_standing[mutation.layer_used] - var/mutable_appearance/indicator_to_add = mutation.get_visual_indicator() - if(!mut_overlay.Find(indicator_to_add)) //either we lack the visual indicator or we have the wrong one - remove_overlay(mutation.layer_used) - for(var/mutable_appearance/indicator_to_remove in mutation.visual_indicators[mutation.type]) - mut_overlay.Remove(indicator_to_remove) - mut_overlay |= indicator_to_add - overlays_standing[mutation.layer_used] = mut_overlay - apply_overlay(mutation.layer_used) + if(!LAZYFIND(species_allowed, new_species.id)) + dna.remove_mutation(src, src.sources) // shouldn't have this mutation at all + +/datum/mutation/proc/on_update_overlay(datum/source, list/overlay_list) + SIGNAL_HANDLER + + var/mutable_appearance/mutation_appearance = mutable_appearance(mutation_icon, mutation_icon_state, -layer_used) + owner.apply_height_offsets(mutation_appearance, offset_location) + + overlay_list += mutation_appearance /** * Called after on_aquiring, or when a chromosome is applied. diff --git a/code/datums/mutations/adaptation.dm b/code/datums/mutations/adaptation.dm index 4228a59376c..7bf6cb12b63 100644 --- a/code/datums/mutations/adaptation.dm +++ b/code/datums/mutations/adaptation.dm @@ -8,17 +8,11 @@ locked = TRUE // fake parent conflicts = list(/datum/mutation/adaptation) mutation_traits = list(TRAIT_WADDLING) - /// Icon used for the adaptation overlay - var/adapt_icon = "meow" + mutation_icon = 'icons/mob/effects/genetics.dmi' /datum/mutation/adaptation/New(datum/mutation/copymut) - ..() + . = ..() conflicts = typesof(/datum/mutation/adaptation) - if(!(type in visual_indicators)) - visual_indicators[type] = list(mutable_appearance('icons/mob/effects/genetics.dmi', adapt_icon, -MUTATIONS_LAYER)) - -/datum/mutation/adaptation/get_visual_indicator() - return visual_indicators[type][1] /datum/mutation/adaptation/cold name = "Cold Adaptation" @@ -26,7 +20,7 @@ text_gain_indication = span_notice("Your body feels refreshingly cold.") instability = POSITIVE_INSTABILITY_MODERATE mutation_traits = list(TRAIT_RESISTCOLD, TRAIT_NO_SLIP_ICE) - adapt_icon = "cold" + mutation_icon_state = "cold" locked = FALSE /datum/mutation/adaptation/heat @@ -35,7 +29,7 @@ text_gain_indication = span_notice("Your body feels invigoratingly warm.") instability = POSITIVE_INSTABILITY_MODERATE mutation_traits = list(TRAIT_RESISTHEAT, TRAIT_ASHSTORM_IMMUNE) - adapt_icon = "fire" + mutation_icon_state = "fire" locked = FALSE /datum/mutation/adaptation/thermal @@ -45,7 +39,7 @@ text_gain_indication = span_notice("Your body feels pleasantly room temperature.") instability = POSITIVE_INSTABILITY_MAJOR mutation_traits = list(TRAIT_RESISTHEAT, TRAIT_RESISTCOLD) - adapt_icon = "thermal" + mutation_icon_state = "thermal" locked = TRUE // recipe /datum/mutation/adaptation/pressure @@ -53,6 +47,6 @@ desc = "A strange mutation that renders the host immune to damage from both low and high pressure environments. Does not protect from temperature, including the cold of space." text_gain_indication = span_notice("Your body feels impressively pressurized.") instability = POSITIVE_INSTABILITY_MODERATE - adapt_icon = "pressure" + mutation_icon_state = "pressure" mutation_traits = list(TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHIGHPRESSURE) locked = FALSE diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm index f38a03f933a..ce494bb3753 100644 --- a/code/datums/mutations/antenna.dm +++ b/code/datums/mutations/antenna.dm @@ -6,6 +6,8 @@ text_lose_indication = span_notice("Your antenna shrinks back down.") instability = POSITIVE_INSTABILITY_MINOR difficulty = 8 + mutation_icon_state = "antenna" + layer_used = FRONT_MUTATIONS_LAYER var/datum/weakref/radio_weakref /obj/item/implant/radio/antenna @@ -33,14 +35,6 @@ if(linked_radio) QDEL_NULL(linked_radio) -/datum/mutation/antenna/New(datum/mutation/copymut) - ..() - if(!(type in visual_indicators)) - visual_indicators[type] = list(mutable_appearance('icons/mob/effects/genetics.dmi', "antenna", -FRONT_MUTATIONS_LAYER+1))//-MUTATIONS_LAYER+1 - -/datum/mutation/antenna/get_visual_indicator() - return visual_indicators[type][1] - /datum/mutation/mindreader name = "Mind Reader" desc = "The affected person can look into the recent memories of others." @@ -51,6 +45,8 @@ instability = POSITIVE_INSTABILITY_MINOR difficulty = 8 locked = TRUE + mutation_icon_state = "antenna" + layer_used = FRONT_MUTATIONS_LAYER /datum/action/cooldown/spell/pointed/mindread name = "Mindread" @@ -174,11 +170,3 @@ log_info += "Current thought: \"[read_text]\"" log_combat(examiner, examined, "mind read (triggered on examine)", null, "info: [english_list(log_info, and_text = ", ")]") - -/datum/mutation/mindreader/New(datum/mutation/copymut) - ..() - if(!(type in visual_indicators)) - visual_indicators[type] = list(mutable_appearance('icons/mob/effects/genetics.dmi', "antenna", -FRONT_MUTATIONS_LAYER+1)) - -/datum/mutation/mindreader/get_visual_indicator() - return visual_indicators[type][1] diff --git a/code/datums/mutations/radioactive.dm b/code/datums/mutations/radioactive.dm index d31b3393fce..9ec68dddfb2 100644 --- a/code/datums/mutations/radioactive.dm +++ b/code/datums/mutations/radioactive.dm @@ -6,17 +6,10 @@ instability = NEGATIVE_STABILITY_MAJOR difficulty = 8 power_coeff = 1 + mutation_icon_state = "radiation" /// Weakref to our radiation emitter component var/datum/weakref/radioactivity_source_ref -/datum/mutation/radioactive/New(datum/mutation/copymut) - . = ..() - if(!(type in visual_indicators)) - visual_indicators[type] = list(mutable_appearance('icons/mob/effects/genetics.dmi', "radiation", -MUTATIONS_LAYER)) - -/datum/mutation/radioactive/get_visual_indicator() - return visual_indicators[type][1] - /datum/mutation/radioactive/on_acquiring(mob/living/carbon/human/acquirer) . = ..() if(!.) diff --git a/code/datums/mutations/reach.dm b/code/datums/mutations/reach.dm index 24f2d5d37e8..2c93ec8e92c 100644 --- a/code/datums/mutations/reach.dm +++ b/code/datums/mutations/reach.dm @@ -7,14 +7,10 @@ text_gain_indication = span_notice("You feel smarter!") limb_req = BODY_ZONE_HEAD instability = POSITIVE_INSTABILITY_MAJOR + mutation_icon_state = "telekinesishead" ///Typecache of atoms that TK shouldn't interact with var/static/list/blacklisted_atoms = typecacheof(list(/atom/movable/screen)) -/datum/mutation/telekinesis/New(datum/mutation/copymut) - ..() - if(!(type in visual_indicators)) - visual_indicators[type] = list(mutable_appearance('icons/mob/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER)) - /datum/mutation/telekinesis/on_acquiring(mob/living/carbon/human/homan) . = ..() if(!.) @@ -27,9 +23,6 @@ return UnregisterSignal(homan, COMSIG_MOB_ATTACK_RANGED) -/datum/mutation/telekinesis/get_visual_indicator() - return visual_indicators[type][1] - ///Triggers on COMSIG_MOB_ATTACK_RANGED. Usually handles stuff like picking up items at range. /datum/mutation/telekinesis/proc/on_ranged_attack(mob/source, atom/target) SIGNAL_HANDLER diff --git a/code/datums/mutations/sight.dm b/code/datums/mutations/sight.dm index 24449177a51..26f85ed0d2c 100644 --- a/code/datums/mutations/sight.dm +++ b/code/datums/mutations/sight.dm @@ -141,11 +141,7 @@ text_gain_indication = span_notice("You feel pressure building up behind your eyes.") layer_used = FRONT_MUTATIONS_LAYER limb_req = BODY_ZONE_HEAD - -/datum/mutation/laser_eyes/New(datum/mutation/copymut) - ..() - if(!(type in visual_indicators)) - visual_indicators[type] = list(mutable_appearance('icons/mob/effects/genetics.dmi', "lasereyes", -FRONT_MUTATIONS_LAYER)) + mutation_icon_state = "lasereyes" /datum/mutation/laser_eyes/on_acquiring(mob/living/carbon/human/H) . = ..() @@ -159,9 +155,6 @@ return UnregisterSignal(H, COMSIG_MOB_ATTACK_RANGED) -/datum/mutation/laser_eyes/get_visual_indicator() - return visual_indicators[type][1] - ///Triggers on COMSIG_MOB_ATTACK_RANGED. Does the projectile shooting. /datum/mutation/laser_eyes/proc/on_ranged_attack(mob/living/carbon/human/source, atom/target, modifiers) SIGNAL_HANDLER diff --git a/code/datums/proximity_monitor/fields/heretic_arena.dm b/code/datums/proximity_monitor/fields/heretic_arena.dm index 432a9dfbd7b..07383a38748 100644 --- a/code/datums/proximity_monitor/fields/heretic_arena.dm +++ b/code/datums/proximity_monitor/fields/heretic_arena.dm @@ -156,6 +156,11 @@ GLOBAL_LIST_EMPTY(heretic_arenas) owner.cut_overlay(crown_overlay) crown_overlay = mutable_appearance('icons/mob/effects/crown.dmi', "arena_victor", -HALO_LAYER) crown_overlay.pixel_z = 24 + if(ishuman(owner)) + var/mob/living/carbon/human/human_parent = owner + human_parent.apply_height_offsets(crown_overlay, UPPER_BODY) + var/obj/item/bodypart/head/human_head = human_parent.get_bodypart(BODY_ZONE_HEAD) + human_head?.worn_head_offset?.apply_offset(crown_overlay) owner.add_overlay(crown_overlay) owner.remove_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id)) @@ -203,6 +208,11 @@ GLOBAL_LIST_EMPTY(heretic_arenas) owner.add_traits(list(TRAIT_ELDRITCH_ARENA_PARTICIPANT, TRAIT_NO_TELEPORT), TRAIT_STATUS_EFFECT(id)) crown_overlay = mutable_appearance('icons/mob/effects/crown.dmi', "arena_fighter", -HALO_LAYER) crown_overlay.pixel_z = 24 + if(ishuman(owner)) + var/mob/living/carbon/human/human_parent = owner + human_parent.apply_height_offsets(crown_overlay, UPPER_BODY) + var/obj/item/bodypart/head/human_head = human_parent.get_bodypart(BODY_ZONE_HEAD) + human_head?.worn_head_offset?.apply_offset(crown_overlay) owner.add_overlay(crown_overlay) return TRUE diff --git a/code/datums/status_effects/buffs/cult_halo.dm b/code/datums/status_effects/buffs/cult_halo.dm new file mode 100644 index 00000000000..bf7c1a92f8a --- /dev/null +++ b/code/datums/status_effects/buffs/cult_halo.dm @@ -0,0 +1,52 @@ +/// Adds the cult halo effect vfx to the mob (instantly/after a delay) +/datum/status_effect/cult_halo + id = "cult_halo" + alert_type = null + tick_interval = STATUS_EFFECT_NO_TICK + /// Cooldown for when the halo is actually visible + COOLDOWN_DECLARE(halo_start) + /// The actual halo applied to the mob + VAR_PRIVATE/mutable_appearance/halo_overlay + +/datum/status_effect/cult_halo/on_creation(mob/living/new_owner, initial_delay = 20 SECONDS) + COOLDOWN_START(src, halo_start, initial_delay) + return ..() + +/datum/status_effect/cult_halo/on_apply() + RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(add_halo)) + if(COOLDOWN_FINISHED(src, halo_start)) + refresh_halo() + else + addtimer(CALLBACK(src, PROC_REF(refresh_halo)), COOLDOWN_TIMELEFT(src, halo_start), TIMER_DELETE_ME) + return TRUE + +/datum/status_effect/cult_halo/on_remove() + UnregisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS) + owner.update_appearance(UPDATE_OVERLAYS) + REMOVE_TRAIT(owner, TRAIT_CULT_HALO, TRAIT_STATUS_EFFECT(id)) + halo_overlay = null + return TRUE + +/datum/status_effect/cult_halo/proc/refresh_halo() + COOLDOWN_RESET(src, halo_start) + ADD_TRAIT(owner, TRAIT_CULT_HALO, TRAIT_STATUS_EFFECT(id)) + owner.update_appearance(UPDATE_OVERLAYS) + new /obj/effect/temp_visual/cult/sparks(get_turf(owner), owner.dir) + +/datum/status_effect/cult_halo/proc/add_halo(datum/source, list/overlay_list) + SIGNAL_HANDLER + + if(!COOLDOWN_FINISHED(src, halo_start)) + return + + halo_overlay ||= mutable_appearance('icons/mob/effects/halo.dmi', "halo[rand(1, 6)]", -HALO_LAYER) + halo_overlay.pixel_z = 0 + halo_overlay.pixel_w = 0 + if (ishuman(owner)) + var/mob/living/carbon/human/human_parent = owner + human_parent.apply_height_offsets(halo_overlay, UPPER_BODY) + + var/obj/item/bodypart/head/human_head = human_parent.get_bodypart(BODY_ZONE_HEAD) + human_head?.worn_head_offset?.apply_offset(halo_overlay) + + overlay_list += halo_overlay diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index bbd451584d2..ada037b02be 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -68,7 +68,7 @@ if(ishuman(outlined_atom)) //humans are special, we want to exclude things like wounds so the outline isn't animated. var/mob/living/carbon/human/human_outline = outlined_atom - add_overlay(human_outline.get_overlays_copy(list(WOUND_LAYER, HALO_LAYER))) + add_overlay(human_outline.get_overlays_copy(list(WOUND_LAYER))) else icon = outlined_atom.icon icon_state = outlined_atom.icon_state diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index ee4b16dc10d..03315f2ad4d 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -225,7 +225,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) to_chat(race_changer, span_notice("Invalid color. Your color is not bright enough.")) race_changer.update_body(is_creating = TRUE) - race_changer.update_mutations_overlay() // no hulk lizard /// Hook for mirrors to do stuff on species change /obj/structure/mirror/proc/on_species_change(mob/living/carbon/human/race_changer, datum/species/newrace) @@ -261,7 +260,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28) sexy.dna.update_ui_block(/datum/dna_block/identity/gender) sexy.update_body(is_creating = TRUE) // or else physique won't change properly - sexy.update_mutations_overlay() //(hulk male/female) sexy.update_clothing(ITEM_SLOT_ICLOTHING) // update gender shaped clothing /obj/structure/mirror/proc/change_eyes(mob/living/carbon/human/user) diff --git a/code/modules/antagonists/cult/datums/cult_team.dm b/code/modules/antagonists/cult/datums/cult_team.dm index 939938f621c..05d4e5bc3f4 100644 --- a/code/modules/antagonists/cult/datums/cult_team.dm +++ b/code/modules/antagonists/cult/datums/cult_team.dm @@ -66,7 +66,7 @@ if(mind.current) SEND_SOUND(mind.current, 'sound/music/antag/bloodcult/bloodcult_halos.ogg') to_chat(mind.current, span_cult_large(span_warning("Your cult is ascendant and the red harvest approaches - you cannot hide your true nature for much longer!!"))) - mind.current.AddElement(/datum/element/cult_halo) + mind.current.apply_status_effect(/datum/status_effect/cult_halo) cult_ascendent = TRUE log_game("The blood cult has ascended with [cultplayers] players.") #endif diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm index de1d5ad2978..cb853d1b358 100644 --- a/code/modules/antagonists/cult/datums/cultist.dm +++ b/code/modules/antagonists/cult/datums/cultist.dm @@ -67,7 +67,7 @@ if(cult_team.cult_risen) current.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS) if(cult_team.cult_ascendent) - current.AddElement(/datum/element/cult_halo, initial_delay = 0 SECONDS) + current.apply_status_effect(/datum/status_effect/cult_halo, 0 SECONDS) ADD_TRAIT(current, TRAIT_HEALS_FROM_CULT_PYLONS, CULT_TRAIT) add_team_hud(current) @@ -85,8 +85,7 @@ if (HAS_TRAIT(current, TRAIT_UNNATURAL_RED_GLOWY_EYES)) current.RemoveElement(/datum/element/cult_eyes) - if (HAS_TRAIT(current, TRAIT_CULT_HALO)) - current.RemoveElement(/datum/element/cult_halo) + current.remove_status_effect(/datum/status_effect/cult_halo) REMOVE_TRAIT(current, TRAIT_HEALS_FROM_CULT_PYLONS, CULT_TRAIT) diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm index 92ac46acfa6..cec6a4b16e1 100644 --- a/code/modules/deathmatch/deathmatch_loadouts.dm +++ b/code/modules/deathmatch/deathmatch_loadouts.dm @@ -925,7 +925,7 @@ /datum/outfit/deathmatch_loadout/cultish/pre_equip(mob/living/carbon/human/user, visuals_only) . = ..() ADD_TRAIT(user, TRAIT_ACT_AS_CULTIST, REF(src)) - user.AddElement(/datum/element/cult_halo, initial_delay = 0 SECONDS) + user.apply_status_effect(/datum/status_effect/cult_halo, 0 SECONDS) user.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS) // Cultist Invoker, has all the balanced cult gear diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index e15594a8eec..0b1830af621 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -64,7 +64,6 @@ There are several things that need to be remembered: update_pockets() update_worn_neck() update_transform() - update_mutations_overlay() update_damage_overlays() // These are done via parent call update_body(), keeping them here for clarity // update_hair() diff --git a/tgstation.dme b/tgstation.dme index fb417168f4b..584b5c9bde6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1588,7 +1588,6 @@ #include "code\datums\elements\cuffable_item.dm" #include "code\datums\elements\cuffsnapping.dm" #include "code\datums\elements\cult_eyes.dm" -#include "code\datums\elements\cult_halo.dm" #include "code\datums\elements\curse_announcement.dm" #include "code\datums\elements\cursed.dm" #include "code\datums\elements\damage_threshold.dm" @@ -2088,6 +2087,7 @@ #include "code\datums\status_effects\song_effects.dm" #include "code\datums\status_effects\stacking_effect.dm" #include "code\datums\status_effects\wound_effects.dm" +#include "code\datums\status_effects\buffs\cult_halo.dm" #include "code\datums\status_effects\buffs\stop_drop_roll.dm" #include "code\datums\status_effects\buffs\stun_absorption.dm" #include "code\datums\status_effects\buffs\xray.dm"