From 845ed7459e6e71a148018a828825bf03e4158355 Mon Sep 17 00:00:00 2001 From: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:35:11 +0100 Subject: [PATCH] Bravo Six, Going Dark: Refactors client colors to fix dupe issues and support filters (#89843) ## About The Pull Request Completely refactored how client colors are handled. Now they're similar to traits, having a source associated with them. Instead of adding and removing by strict type (which makes client colors prone to getting duplicated and not cleaned up) you remove a filter associated with a specific source. Adding another client color with the same source as an already existing one will replace the existing one if its of a different type, or do nothing if they're the same (unless force is set to TRUE). Client colors can also force filter splitting, putting all colors that come before them, themselves, and all colors after them into separate filters - this is useful to prevent mixing in filters which are supposed to remove a certain color.
Example of how Perceptomatrix and nightmare vision goggles combined before this PR: ![dreamseeker_fBOse2i0jq](https://github.com/user-attachments/assets/fb164e1f-ce8b-4a8c-8820-5db25e710056) And this is after, as you can see nightmare vision effect's red is only slightly tinted by perceptomatix instead of being literally halved. ![dreamseeker_U6A24fetK8](https://github.com/user-attachments/assets/8d8b3eb5-9f53-4646-bc56-0f5897013c6f)
Additionally, added support for custom filters (and not just colors) to client color code to allow us to work with more colorspaces. Also fixed weird blindness behavior, so this also Closes #89787 ## Why It's Good For The Game Makes code less ass to work with, fixes weird color mixing, etc. ## Changelog :cl: fix: Fixed perceptomatix helmet allowing you to see even when unconscious refactor: Refactored how client colors are handled, ensuring that certain effects like nightmare goggles don't disappear when another vision-affecting piece of clothing is worn. /:cl: --- code/__DEFINES/colors.dm | 10 + code/__HELPERS/cmp.dm | 4 + code/__HELPERS/colors.dm | 8 +- code/__HELPERS/spatial_info.dm | 1 + code/datums/brain_damage/mild.dm | 4 +- code/datums/components/echolocation.dm | 10 +- code/datums/components/manual_heart.dm | 10 +- .../datums/elements/wearable_client_colour.dm | 13 +- .../quirks/neutral_quirks/monochromatic.dm | 4 +- code/datums/status_effects/buffs.dm | 2 +- code/datums/status_effects/debuffs/debuffs.dm | 4 +- .../debuffs/vision/blindness.dm | 39 +- code/modules/client/client_colour.dm | 427 +++++++++--------- code/modules/clothing/glasses/_glasses.dm | 8 +- code/modules/clothing/head/hardhat.dm | 4 +- code/modules/clothing/head/perceptomatrix.dm | 7 +- code/modules/clothing/suits/costume.dm | 2 +- .../mining_loot/megafauna/bubblegum.dm | 2 +- code/modules/mob/mob_defines.dm | 3 + code/modules/mod/modules/modules_maint.dm | 6 +- .../xenobiology/crossbreeding/_clothing.dm | 6 +- .../surgery/bodyparts/robot_bodyparts.dm | 5 +- code/modules/unit_tests/blindness.dm | 4 +- code/modules/unit_tests/client_colours.dm | 10 +- 24 files changed, 322 insertions(+), 271 deletions(-) diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index ee1ebd0cfe1..59af9d98210 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -488,3 +488,13 @@ GLOBAL_LIST_INIT(heretic_path_to_color, list( #define EYE_COLOR_WEED_PRIORITY 20 #define EYE_COLOR_LUMINESCENT_PRIORITY 30 #define EYE_COLOR_CULT_PRIORITY 40 + +// Client color priorities + +#define CLIENT_COLOR_GLASSES_PRIORITY 1 // Lowest there is, used by glasses +#define CLIENT_COLOR_HELMET_PRIORITY 2 // Same but for helmets +#define CLIENT_COLOR_ORGAN_PRIORITY 3 // For heads and organs +#define CLIENT_COLOR_FILTER_PRIORITY 4 // Filters which should go ontop of previous ones +#define CLIENT_COLOR_TEMPORARY_PRIORITY 5 // Temporary flashing effects +#define CLIENT_COLOR_IMPORTANT_PRIORITY 6 // Gameplay important hints signifying antag status or near-death, should be always shown +#define CLIENT_COLOR_OVERRIDE_PRIORITY 7 // For effects that are meant to mask all others for technical reasons diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 21a5f00e31b..f333318c94e 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -231,3 +231,7 @@ /// Orders cameras by their `c_tag` ascending /proc/cmp_camera_ctag_asc(obj/machinery/camera/a, obj/machinery/camera/b) return sorttext(b.c_tag, a.c_tag) + +/// Sorts client colors based on their priority +/proc/cmp_client_colours(datum/client_colour/first_color, datum/client_colour/second_color) + return second_color.priority - first_color.priority diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm index 28677669768..8e9ac7def5b 100644 --- a/code/__HELPERS/colors.dm +++ b/code/__HELPERS/colors.dm @@ -47,6 +47,8 @@ var/list/color = rgb2num(HTMLstring) return rgb(255 - color[1], 255 - color[2], 255 - color[3]) +#define TEMP_COLOR_SOURCE "temp_flash" + ///Flash a color on the passed mob /proc/flash_color(mob_or_client, flash_color=COLOR_CULT_RED, flash_time=20) var/mob/flashed_mob @@ -60,11 +62,13 @@ return var/datum/client_colour/temp/temp_color = new(flashed_mob) - temp_color.colour = flash_color + temp_color.color = flash_color temp_color.fade_in = flash_time * 0.25 temp_color.fade_out = flash_time * 0.25 QDEL_IN(temp_color, (flash_time * 0.5) + 1) - flashed_mob.add_client_colour(temp_color) + flashed_mob.add_client_colour(temp_color, TEMP_COLOR_SOURCE) + +#undef TEMP_COLOR_SOURCE /// Blends together two colors (passed as 3 or 4 length lists) using the screen blend mode /// Much like multiply, screen effects the brightness of the resulting color diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm index 346ef4d64f8..4cc71bb091b 100644 --- a/code/__HELPERS/spatial_info.dm +++ b/code/__HELPERS/spatial_info.dm @@ -26,6 +26,7 @@ alerts = null screens = null client_colours = null + color_filter_store = null hud_possible = null /// references to everything "on" the turf we are assigned to, that we care about. populated in assign() and cleared in unassign(). /// movables iside of other movables count as being "on" if they have get_turf(them) == our turf. intentionally not a lazylist diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 2af37fa13a8..846869901d6 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -280,11 +280,11 @@ lose_text = span_notice("The world feels bright and colorful again.") /datum/brain_trauma/mild/color_blindness/on_gain() - owner.add_client_colour(/datum/client_colour/monochrome/colorblind) + owner.add_client_colour(/datum/client_colour/monochrome, TRAUMA_TRAIT) return ..() /datum/brain_trauma/mild/color_blindness/on_lose(silent) - owner.remove_client_colour(/datum/client_colour/monochrome/colorblind) + owner.remove_client_colour(TRAUMA_TRAIT) return ..() /datum/brain_trauma/mild/possessive diff --git a/code/datums/components/echolocation.dm b/code/datums/components/echolocation.dm index 4fda54ac0f5..2600d77d7ba 100644 --- a/code/datums/components/echolocation.dm +++ b/code/datums/components/echolocation.dm @@ -16,7 +16,7 @@ /// This trait blocks us from receiving echolocation. var/blocking_trait /// Ref of the client color we give to the echolocator. - var/client_color + var/client_colour /// Associative list of receivers to lists of atoms they are rendering (those atoms are associated to data of the image and time they were rendered at). var/list/receivers = list() /// All the saved appearances, keyed by icon-icon_state. @@ -55,10 +55,10 @@ src.images_are_static = images_are_static if(!isnull(blocking_trait)) src.blocking_trait = blocking_trait - if(ispath(color_path)) - client_color = echolocator.add_client_colour(color_path) src.echo_group = echo_group || REF(src) - echolocator.add_traits(list(TRAIT_ECHOLOCATION_RECEIVER, TRAIT_TRUE_NIGHT_VISION), echo_group) //so they see all the tiles they echolocated, even if they are in the dark + if(ispath(color_path)) + client_colour = echolocator.add_client_colour(color_path, src.echo_group) + echolocator.add_traits(list(TRAIT_ECHOLOCATION_RECEIVER, TRAIT_TRUE_NIGHT_VISION), src.echo_group) //so they see all the tiles they echolocated, even if they are in the dark echolocator.become_blind(ECHOLOCATION_TRAIT) echolocator.overlay_fullscreen("echo", /atom/movable/screen/fullscreen/echo, echo_icon) START_PROCESSING(SSfastprocess, src) @@ -66,7 +66,7 @@ /datum/component/echolocation/Destroy(force) STOP_PROCESSING(SSfastprocess, src) var/mob/living/echolocator = parent - QDEL_NULL(client_color) + QDEL_NULL(client_colour) echolocator.remove_traits(list(TRAIT_ECHOLOCATION_RECEIVER, TRAIT_TRUE_NIGHT_VISION), echo_group) echolocator.cure_blind(ECHOLOCATION_TRAIT) echolocator.clear_fullscreen("echo") diff --git a/code/datums/components/manual_heart.dm b/code/datums/components/manual_heart.dm index d8d74854900..fb815cdfdbb 100644 --- a/code/datums/components/manual_heart.dm +++ b/code/datums/components/manual_heart.dm @@ -69,7 +69,7 @@ to_chat(parent, span_userdanger("You feel your heart start beating normally again!")) var/mob/living/carbon/carbon_parent = parent if(istype(carbon_parent)) - carbon_parent.remove_client_colour(/datum/client_colour/manual_heart_blood) + carbon_parent.remove_client_colour(REF(src)) /datum/component/manual_heart/proc/restart() SIGNAL_HANDLER @@ -85,7 +85,7 @@ pump_action.build_all_button_icons(UPDATE_BUTTON_STATUS) var/mob/living/carbon/carbon_parent = parent if(istype(carbon_parent)) - carbon_parent.remove_client_colour(/datum/client_colour/manual_heart_blood) //prevents red overlay from getting stuck + carbon_parent.remove_client_colour(REF(src)) //prevents red overlay from getting stuck STOP_PROCESSING(SSdcs, src) /// Worker proc that checks logic for if a pump can happen, and applies effects from doing so @@ -98,7 +98,7 @@ if(HAS_TRAIT(carbon_owner, TRAIT_NOBLOOD)) return carbon_owner.blood_volume = min(carbon_owner.blood_volume + (blood_loss * 0.5), BLOOD_VOLUME_MAXIMUM) - carbon_owner.remove_client_colour(/datum/client_colour/manual_heart_blood) + carbon_owner.remove_client_colour(REF(src)) add_colour = TRUE carbon_owner.adjustBruteLoss(-heal_brute) carbon_owner.adjustFireLoss(-heal_burn) @@ -119,7 +119,7 @@ to_chat(carbon_parent, span_userdanger("You have to keep pumping your blood!")) COOLDOWN_START(src, heart_timer, MANUAL_HEART_GRACE_PERIOD) //give two full seconds before losing more blood if(add_colour) - carbon_parent.add_client_colour(/datum/client_colour/manual_heart_blood) + carbon_parent.add_client_colour(/datum/client_colour/manual_heart_blood, REF(src)) add_colour = FALSE ///If a new heart is added, start processing. @@ -134,7 +134,7 @@ pump_action.build_all_button_icons(UPDATE_BUTTON_STATUS) var/mob/living/carbon/carbon_parent = parent if(istype(carbon_parent)) - carbon_parent.remove_client_colour(/datum/client_colour/manual_heart_blood) //prevents red overlay from getting stuck + carbon_parent.remove_client_colour(REF(src)) //prevents red overlay from getting stuck START_PROCESSING(SSdcs, src) ///If the heart is removed, stop processing. diff --git a/code/datums/elements/wearable_client_colour.dm b/code/datums/elements/wearable_client_colour.dm index 8757dd1098c..d2fde7cffc4 100644 --- a/code/datums/elements/wearable_client_colour.dm +++ b/code/datums/elements/wearable_client_colour.dm @@ -6,6 +6,8 @@ var/datum/client_colour/colour_type ///The slot(s) that enable the client colour var/equip_slots = NONE + ///Source for the client colour + var/colour_source ///For items that want costumizable client colours var/custom_colour ///if forced is false, we check that the user has the TRAIT_SEE_WORN_COLOURS before adding the colour. @@ -13,7 +15,7 @@ ///On examine, it'll tell which you have to press to toggle TRAIT_SEE_WORN_COLOURS. var/key_info = "Figure it out yourself how" -/datum/element/wearable_client_colour/Attach(obj/item/target, colour_type, equip_slots, custom_colour, forced = FALSE, comsig_toggle = COMSIG_CLICK_ALT) +/datum/element/wearable_client_colour/Attach(obj/item/target, colour_type, equip_slots, colour_source, custom_colour, forced = FALSE, comsig_toggle = COMSIG_CLICK_ALT) . = ..() if(!isitem(target)) return ELEMENT_INCOMPATIBLE @@ -23,6 +25,7 @@ src.colour_type = colour_type src.equip_slots = equip_slots + src.colour_source = colour_source src.custom_colour = custom_colour src.forced = forced @@ -90,19 +93,19 @@ var/datum/client_colour/colour_to_add = colour_type if(custom_colour) colour_to_add = new colour_to_add - colour_to_add.colour = custom_colour - equipper.add_client_colour(colour_to_add) + colour_to_add.color = custom_colour + equipper.add_client_colour(colour_to_add, colour_source) /datum/element/wearable_client_colour/proc/on_trait_removed(mob/source, trait) SIGNAL_HANDLER - source.remove_client_colour(colour_type) + source.remove_client_colour(colour_source) /datum/element/wearable_client_colour/proc/remove_client_colour(mob/dropper) if(!forced) UnregisterSignal(dropper, list(SIGNAL_ADDTRAIT(TRAIT_SEE_WORN_COLOURS), SIGNAL_REMOVETRAIT(TRAIT_SEE_WORN_COLOURS))) if(!HAS_TRAIT(dropper, TRAIT_SEE_WORN_COLOURS)) return - dropper.remove_client_colour(colour_type) + dropper.remove_client_colour(colour_source) /datum/element/wearable_client_colour/proc/toggle_see_worn_colors(obj/item/source, mob/clicker) SIGNAL_HANDLER diff --git a/code/datums/quirks/neutral_quirks/monochromatic.dm b/code/datums/quirks/neutral_quirks/monochromatic.dm index 6c1b8c2ef78..3295ca321a8 100644 --- a/code/datums/quirks/neutral_quirks/monochromatic.dm +++ b/code/datums/quirks/neutral_quirks/monochromatic.dm @@ -12,7 +12,7 @@ ) /datum/quirk/monochromatic/add(client/client_source) - quirk_holder.add_client_colour(/datum/client_colour/monochrome) + quirk_holder.add_client_colour(/datum/client_colour/monochrome, QUIRK_TRAIT) /datum/quirk/monochromatic/post_add() if(is_detective_job(quirk_holder.mind.assigned_role)) @@ -20,4 +20,4 @@ quirk_holder.playsound_local(quirk_holder, 'sound/ambience/security/ambidet1.ogg', 50, FALSE) /datum/quirk/monochromatic/remove() - quirk_holder.remove_client_colour(/datum/client_colour/monochrome) + quirk_holder.remove_client_colour(QUIRK_TRAIT) diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index f75516c9868..8a82f4fba05 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -436,7 +436,7 @@ owner.log_message("entered a blood frenzy", LOG_ATTACK) to_chat(owner, span_narsiesmall("KILL, KILL, KILL! YOU HAVE NO ALLIES ANYMORE, NO TEAM MATES OR ALLEGIANCES! KILL THEM ALL!")) - var/datum/client_colour/colour = owner.add_client_colour(/datum/client_colour/bloodlust) + var/datum/client_colour/colour = owner.add_client_colour(/datum/client_colour/bloodlust, REF(src)) QDEL_IN(colour, 1.1 SECONDS) return TRUE diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 1d089ddbb39..91577046210 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -574,7 +574,7 @@ return FALSE RegisterSignal(owner, COMSIG_MOVABLE_HEAR, PROC_REF(hypnotize)) ADD_TRAIT(owner, TRAIT_MUTE, TRAIT_STATUS_EFFECT(id)) - owner.add_client_colour(/datum/client_colour/monochrome/trance) + owner.add_client_colour(/datum/client_colour/monochrome, REF(src)) owner.visible_message("[stun ? span_warning("[owner] stands still as [owner.p_their()] eyes seem to focus on a distant point.") : ""]", \ span_warning(pick("You feel your thoughts slow down...", "You suddenly feel extremely dizzy...", "You feel like you're in the middle of a dream...","You feel incredibly relaxed..."))) return TRUE @@ -588,7 +588,7 @@ UnregisterSignal(owner, COMSIG_MOVABLE_HEAR) REMOVE_TRAIT(owner, TRAIT_MUTE, TRAIT_STATUS_EFFECT(id)) owner.remove_status_effect(/datum/status_effect/dizziness) - owner.remove_client_colour(/datum/client_colour/monochrome/trance) + owner.remove_client_colour(REF(src)) to_chat(owner, span_warning("You snap out of your trance!")) /datum/status_effect/trance/get_examine_text() diff --git a/code/datums/status_effects/debuffs/vision/blindness.dm b/code/datums/status_effects/debuffs/vision/blindness.dm index c0e5249cad6..caf8e98a2b0 100644 --- a/code/datums/status_effects/debuffs/vision/blindness.dm +++ b/code/datums/status_effects/debuffs/vision/blindness.dm @@ -4,6 +4,8 @@ /// Blindness /datum/status_effect/grouped/blindness + // This is not "remove on fullheal" as in practice, + // fullheal should instead remove all the sources and in turn cure this id = "blindness" tick_interval = STATUS_EFFECT_NO_TICK alert_type = /atom/movable/screen/alert/status_effect/blind @@ -11,37 +13,52 @@ SIGNAL_REMOVETRAIT(TRAIT_SIGHT_BYPASS), SIGNAL_ADDTRAIT(TRAIT_SIGHT_BYPASS), ) - // This is not "remove on fullheal" as in practice, - // fullheal should instead remove all the sources and in turn cure this + /// List of sources which prevent SIGHT_BYPASS from working + var/static/list/blocking_sources = list( + QUIRK_TRAIT, // Meant to be completely immutable + ECHOLOCATION_TRAIT, // Breaks the UI badly + UNCONSCIOUS_TRAIT, // Duh + ) /datum/status_effect/grouped/blindness/on_apply() - if(!CAN_BE_BLIND(owner)) + if (!CAN_BE_BLIND(owner)) return FALSE RegisterSignals(owner, update_signals, PROC_REF(update_blindness)) - update_blindness() - return ..() +/datum/status_effect/grouped/blindness/source_added(source, ...) + update_blindness() + +/datum/status_effect/grouped/blindness/source_removed(source, removing) + if (!removing) + update_blindness() + /datum/status_effect/grouped/blindness/proc/update_blindness() - if(!CAN_BE_BLIND(owner)) // future proofing + if (!CAN_BE_BLIND(owner)) // future proofing qdel(src) return - if(HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS)) - make_unblind() + if (!HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS)) + make_blind() return - make_blind() + + for (var/blocker in blocking_sources) + if (owner.is_blind_from(blocker)) + make_blind() + return + + make_unblind() /datum/status_effect/grouped/blindness/proc/make_blind() owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind) // You are blind - at most, able to make out shapes near you - owner.add_client_colour(/datum/client_colour/monochrome/blind) + owner.add_client_colour(/datum/client_colour/monochrome, REF(src)) /datum/status_effect/grouped/blindness/proc/make_unblind() owner.clear_fullscreen(id) - owner.remove_client_colour(/datum/client_colour/monochrome/blind) + owner.remove_client_colour(REF(src)) /datum/status_effect/grouped/blindness/on_remove() make_unblind() diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm index 08e79c305a4..2bee4dda956 100644 --- a/code/modules/client/client_colour.dm +++ b/code/modules/client/client_colour.dm @@ -1,35 +1,21 @@ -#define PRIORITY_ABSOLUTE 1 -#define PRIORITY_HIGH 10 -#define PRIORITY_NORMAL 100 -#define PRIORITY_LOW 1000 +#define CLIENT_COLOR_VALUE_INDEX 1 +#define CLIENT_COLOR_PRIORITY_INDEX 2 -/** - * Client Colour Priority System By RemieRichards (then refactored by another contributor) - * A System that gives finer control over which client.colour value to display on screen - * so that the "highest priority" one is always displayed as opposed to the default of - * "whichever was set last is displayed". - * - * Refactored to allow multiple overlapping client colours - * (e.g. wearing blue glasses under a yellow visor, even though the result is a little unsatured.) - * As well as some support for animated colour transitions. - * - * Define subtypes of this datum - */ /datum/client_colour - ///The color we want to give to the client. This has to be either a hexadecimal color or a color matrix. - var/colour - ///The mob that owns this client_colour. + /// Color given to the client, can be a hex color, color matrix or a filter + var/color + /// The mob that owns this client_colour var/mob/owner - /** - * We prioritize colours with higher priority (lower numbers), so they don't get overriden by less important ones: - * eg: "Bloody screen" > "goggles colour" as the former is much more important - */ - var/priority = PRIORITY_NORMAL - ///Will this client_colour prevent ones of lower priority from being applied? + /// Priority of this color, higher values are rendered above lower ones + var/priority = CLIENT_COLOR_FILTER_PRIORITY + /// Will this client_colour prevent ones of lower priority from being applied? var/override = FALSE - ///IF non-zero, 'animate_client_colour(fade_in)' will be called instead of 'update_client_colour' when added. + /// If set to TRUE, all colors below and above this one will be rendered in separate filters + /// If color is a filter, forced to TRUE + var/split_filters = FALSE + /// If non-zero, 'animate_client_colour(fade_in)' will be called instead of 'update_client_colour' when added. var/fade_in = 0 - ///Same as above, but on removal. + /// If non-zero, 'animate_client_colour(fade_out)' will be called instead of 'update_client_colour' when removed. var/fade_out = 0 /datum/client_colour/New(mob/owner) @@ -42,229 +28,248 @@ owner = null return ..() -///Sets a new colour, then updates the owner's screen colour. -/datum/client_colour/proc/update_colour(new_colour, anim_time, easing = 0) - colour = new_colour +///Sets a new color, then updates the owner's screen color. +/datum/client_colour/proc/update_color(new_color, anim_time, easing = 0) + color = new_color owner.animate_client_colour(anim_time, easing) /** - * Adds an instance of colour_type to the mob's client_colours list - * colour_type - a typepath (subtyped from /datum/client_colour) + * Add a color filter to the client + * new_color - client_colour datum or typepath to be added + * source - associated source for the client color + * force - if TRUE, colors of the same source will be replaced even if it is of the same type */ -/mob/proc/add_client_colour(colour_type_or_datum) - if(QDELING(src)) +/mob/proc/add_client_colour(datum/client_colour/new_color, source, force = FALSE) + if (QDELING(src)) return - var/datum/client_colour/colour - if(istype(colour_type_or_datum, /datum/client_colour)) - colour = colour_type_or_datum - else if(ispath(colour_type_or_datum, /datum/client_colour)) - colour = new colour_type_or_datum(src) - else - CRASH("Invalid colour type or datum for add_client_color: [colour_type_or_datum || "null"]") - BINARY_INSERT(colour, client_colours, /datum/client_colour, colour, priority, COMPARE_KEY) - animate_client_colour(colour.fade_in) - return colour + if (ispath(new_color)) + new_color = new new_color(src) + + if (!istype(new_color)) + CRASH("Invalid color type or datum for add_client_colour: [new_color ? "[new_color] ([new_color.type])" : "null"]") + + // Ensure that if a color with this source is already present, we either abort or get rid of it + var/datum/client_colour/existing_color = get_client_colour(source) + if (existing_color) + if (existing_color.type == new_color.type && !force) + return existing_color + qdel(existing_color) + client_colours[new_color] = source + animate_client_colour(new_color.fade_in) + return new_color /** - * Removes an instance of colour_type from the mob's client_colours list - * colour_type - a typepath (subtyped from /datum/client_colour) - */ -/mob/proc/remove_client_colour(colour_type) - if(!ispath(colour_type, /datum/client_colour)) - return + * Removes a color type from a specific source from mob's client_colours list + * source - color source to remove +*/ - for(var/datum/client_colour/colour as anything in client_colours) - if(colour.type == colour_type) - qdel(colour) +/mob/proc/remove_client_colour(source) + var/datum/client_colour/existing_color = get_client_colour(source) + if (!existing_color) + return FALSE + qdel(existing_color) + return TRUE + +/mob/proc/get_client_colour(source) + for(var/datum/client_colour/color as anything in client_colours) + if (client_colours[color] == source) + return color + +/mob/proc/get_client_colour_filters() + . = list() + // sortTim sorts the passed list instead of making the copy, and so does reverse_range + var/list/used_colors = reverse_range(sortTim(client_colours.Copy(), GLOBAL_PROC_REF(cmp_client_colours))) + var/current_color = null + var/color_num = 0 + var/color_prio = 1 + + for (var/datum/client_colour/client_color as anything in used_colors) + color_num += 1 + + var/list/filter_color = null + if (islist(client_color.color)) + filter_color = client_color.color + // If our list has "type" in it then its a filter + if (!filter_color["type"]) + filter_color = null + + if (client_color.split_filters || filter_color) + if (current_color) + . += list(list(color_matrix_filter(current_color), color_prio)) + color_prio += 1 + current_color = null + + . += list(list(filter_color || color_matrix_filter(client_color.color), color_prio)) + color_prio += 1 + continue + + if (!current_color) + current_color = client_color.color + if (client_color.override) + break + continue + + var/list/color_list = current_color + if (!islist(color_list)) + color_list = color_to_full_rgba_matrix(color_list) + var/list/cur_list = color_to_full_rgba_matrix(client_color.color) + + for (var/i in 1 to 20) + color_list[i] = (color_list[i] * (color_num - 1) + cur_list[i]) / color_num + current_color = color_list + + if (client_color.override) break -/** - * Gets the resulting colour/tone from client_colours. - * In the case of multiple colours, they'll be converted to RGBA matrices for compatibility, - * summed together, and then each element divided by the number of matrices. (except we do this with lists because byond) - * target is the target variable. - */ -#define MIX_CLIENT_COLOUR(target)\ - var/_our_colour;\ - var/_number_colours = 0;\ - var/_pool_closed = INFINITY;\ - for(var/_c in client_colours){\ - var/datum/client_colour/_colour = _c;\ - if(_pool_closed < _colour.priority){\ - break\ - };\ - _number_colours++;\ - if(_colour.override){\ - _pool_closed = _colour.priority\ - };\ - if(!_our_colour){\ - _our_colour = _colour.colour;\ - continue\ - };\ - if(_number_colours == 2){\ - _our_colour = color_to_full_rgba_matrix(_our_colour)\ - };\ - var/list/_colour_matrix = color_to_full_rgba_matrix(_colour.colour);\ - var/list/_L = _our_colour;\ - for(var/_i in 1 to 20){\ - _L[_i] += _colour_matrix[_i]\ - };\ - };\ - if(_number_colours > 1){\ - var/list/_L = _our_colour;\ - for(var/_i in 1 to 20){\ - _L[_i] /= _number_colours\ - };\ - };\ - target = _our_colour\ + if (current_color) + . += list(list(color_matrix_filter(current_color), color_prio)) -#define CLIENT_COLOR_FILTER_KEY "fake_client_color" - -/** - * Resets the mob's client.color to null, and then reapplies a new color based - * on the client_colour datums it currently has. - */ /mob/proc/update_client_colour() - if(isnull(hud_used)) + if (isnull(hud_used)) return - var/new_color = "" - if(length(client_colours)) - MIX_CLIENT_COLOUR(new_color) + for (var/atom/movable/screen/plane_master/game_plane as anything in hud_used.get_true_plane_masters(RENDER_PLANE_GAME)) + for (var/filter_id in color_filter_store) + game_plane.remove_filter(filter_id) - for(var/atom/movable/screen/plane_master/game_plane as anything in hud_used.get_true_plane_masters(RENDER_PLANE_GAME)) - if(new_color) - game_plane.add_filter(CLIENT_COLOR_FILTER_KEY, 2, color_matrix_filter(new_color)) - else - game_plane.remove_filter(CLIENT_COLOR_FILTER_KEY) + color_filter_store.Cut() + var/list/applied_filters = get_client_colour_filters() -///Works similarly to 'update_client_colour', but animated. -/mob/proc/animate_client_colour(anim_time = 2 SECONDS, anim_easing = NONE) - if(anim_time <= 0) + for (var/list/color_filter as anything in applied_filters) + var/added_color = color_filter[CLIENT_COLOR_VALUE_INDEX] + var/filter_priority = color_filter[CLIENT_COLOR_PRIORITY_INDEX] + for (var/atom/movable/screen/plane_master/game_plane as anything in hud_used.get_true_plane_masters(RENDER_PLANE_GAME)) + var/filter_id = "client_colour_[filter_priority]" + game_plane.add_filter(filter_id, filter_priority, added_color) + color_filter_store |= filter_id + +/// Works similarly to 'update_client_colour', but animated. +/mob/proc/animate_client_colour(anim_time = 1 SECONDS, anim_easing = NONE) + if (isnull(hud_used)) + return + + if(anim_time <= -1) return update_client_colour() - if(isnull(hud_used)) - return - var/anim_color = "" - if(length(client_colours)) - MIX_CLIENT_COLOUR(anim_color) + for (var/atom/movable/screen/plane_master/game_plane as anything in hud_used.get_true_plane_masters(RENDER_PLANE_GAME)) + for (var/filter_id in color_filter_store) + game_plane.remove_filter(filter_id) - for(var/atom/movable/screen/plane_master/game_plane as anything in hud_used.get_true_plane_masters(RENDER_PLANE_GAME)) - if(anim_color) - game_plane.add_filter(CLIENT_COLOR_FILTER_KEY, 2, color_matrix_filter()) - game_plane.transition_filter(CLIENT_COLOR_FILTER_KEY, color_matrix_filter(anim_color), anim_time, anim_easing) - else - game_plane.transition_filter(CLIENT_COLOR_FILTER_KEY, color_matrix_filter(), anim_time, anim_easing) - // This leaves a blank color filter on the hud which is, fine I guess? + color_filter_store.Cut() + var/list/applied_filters = get_client_colour_filters() -#undef MIX_CLIENT_COLOUR + for (var/list/color_filter as anything in applied_filters) + var/added_color = color_filter[CLIENT_COLOR_VALUE_INDEX] + var/filter_priority = color_filter[CLIENT_COLOR_PRIORITY_INDEX] + for (var/atom/movable/screen/plane_master/game_plane as anything in hud_used.get_true_plane_masters(RENDER_PLANE_GAME)) + var/filter_id = "client_colour_[filter_priority]" + game_plane.add_filter(filter_id, filter_priority, color_matrix_filter()) + game_plane.transition_filter(filter_id, added_color, anim_time, anim_easing) + color_filter_store |= filter_id -#undef CLIENT_COLOR_FILTER_KEY +// Color types -/datum/client_colour/glass_colour - priority = PRIORITY_LOW - -/datum/client_colour/glass_colour/green - colour = "#aaffaa" - -/datum/client_colour/glass_colour/lightgreen - colour = "#ccffcc" - -/datum/client_colour/glass_colour/blue - colour = "#aaaaff" - -/datum/client_colour/glass_colour/lightblue - colour = "#ccccff" - -/datum/client_colour/glass_colour/yellow - colour = "#ffff66" - -/datum/client_colour/glass_colour/lightyellow - colour = "#ffffaa" - -/datum/client_colour/glass_colour/red - colour = "#ffaaaa" - -/datum/client_colour/glass_colour/lightred - colour = "#ffcccc" - -/datum/client_colour/glass_colour/darkred - colour = "#bb5555" - -/datum/client_colour/glass_colour/orange - colour = "#ffbb99" - -/datum/client_colour/glass_colour/lightorange - colour = "#ffddaa" - -/datum/client_colour/glass_colour/purple - colour = "#ff99ff" - -/datum/client_colour/glass_colour/lightpurple - colour = "#ffccff" - -/datum/client_colour/glass_colour/gray - colour = "#cccccc" - -///A client colour that makes the screen look a bit more grungy, halloweenesque even. +///A client color that makes the screen look a bit more grungy, halloweenesque even. /datum/client_colour/halloween_helmet - colour = list(0.75,0.13,0.13,0, 0.13,0.7,0.13,0, 0.13,0.13,0.75,0, -0.06,-0.09,-0.08,1, 0,0,0,0) + priority = CLIENT_COLOR_HELMET_PRIORITY + color = list(/*R*/ 0.75,0.13,0.13,0, /*G*/ 0.13,0.7,0.13,0, /*B*/ 0.13,0.13,0.75,0, /*A*/ -0.06,-0.09,-0.08,1, /*C*/ 0,0,0,0) /datum/client_colour/flash_hood - colour = COLOR_MATRIX_POLAROID - -/datum/client_colour/glass_colour/nightmare - colour = list(255,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, -130,0,0,0) //every color is either red or black - -/datum/client_colour/malfunction - colour = list(/*R*/ 0,0,0,0, /*G*/ 0,175,0,0, /*B*/ 0,0,0,0, /*A*/ 0,0,0,1, /*C*/0,-130,0,0) // Matrix colors + priority = CLIENT_COLOR_HELMET_PRIORITY + color = COLOR_MATRIX_POLAROID /datum/client_colour/perceptomatrix - colour = list(/*R*/ 1,0,0,0, /*G*/ 0,1,0,0, /*B*/ 0,0,1,0, /*A*/ 0,0,0,1, /*C*/0,-0.02,-0.02,0) // veeery slightly pink + priority = CLIENT_COLOR_HELMET_PRIORITY + color = list(/*R*/ 1,0,0,0, /*G*/ 0,1,0,0, /*B*/ 0,0,1,0, /*A*/ 0,0,0,1, /*C*/ 0,-0.02,-0.02,0) // veeery slightly pink + +/datum/client_colour/rave + priority = CLIENT_COLOR_HELMET_PRIORITY + +/datum/client_colour/malfunction + priority = CLIENT_COLOR_ORGAN_PRIORITY + color = list(/*R*/ 0,0,0,0, /*G*/ 0,175,0,0, /*B*/ 0,0,0,0, /*A*/ 0,0,0,1, /*C*/ 0,-130,0,0) // Matrix colors /datum/client_colour/monochrome - colour = COLOR_MATRIX_GRAYSCALE - priority = PRIORITY_HIGH //we can't see colors anyway! - override = TRUE - fade_in = 20 - fade_out = 20 + color = COLOR_MATRIX_GRAYSCALE + priority = CLIENT_COLOR_FILTER_PRIORITY + split_filters = TRUE + fade_in = 2 SECONDS + fade_out = 2 SECONDS -/datum/client_colour/monochrome/colorblind - priority = PRIORITY_HIGH - -/datum/client_colour/monochrome/trance - priority = PRIORITY_NORMAL - -/datum/client_colour/monochrome/blind - priority = PRIORITY_NORMAL +/datum/client_colour/monochrome/glasses + priority = CLIENT_COLOR_GLASSES_PRIORITY /datum/client_colour/bloodlust - priority = PRIORITY_ABSOLUTE // Only anger. - colour = list(0,0,0,0,0,0,0,0,0,1,0,0) //pure red. - fade_out = 10 + priority = CLIENT_COLOR_IMPORTANT_PRIORITY + color = list(0,0,0,0,0,0,0,0,0,1,0,0) // pure red + fade_out = 1 SECONDS /datum/client_colour/bloodlust/New(mob/owner) ..() if(owner) - addtimer(CALLBACK(src, PROC_REF(update_colour), list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0), 10, SINE_EASING|EASE_OUT), 0.1 SECONDS) - -/datum/client_colour/rave - priority = PRIORITY_LOW - -/datum/client_colour/psyker - priority = PRIORITY_ABSOLUTE - override = TRUE - colour = list(0.8,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0) + addtimer(CALLBACK(src, PROC_REF(update_color), list(/*R*/ 1,0,0, /*G*/ 0.8,0.2,0, /*B*/ 0.8,0,0.2, /*C*/ 0.1,0,0), 10, SINE_EASING|EASE_OUT), 0.1 SECONDS) /datum/client_colour/manual_heart_blood - priority = PRIORITY_ABSOLUTE - colour = COLOR_RED + priority = CLIENT_COLOR_IMPORTANT_PRIORITY + color = COLOR_RED + +/datum/client_colour/psyker + priority = CLIENT_COLOR_OVERRIDE_PRIORITY + color = list(0.8,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0) + override = TRUE /datum/client_colour/temp - priority = PRIORITY_HIGH + priority = CLIENT_COLOR_TEMPORARY_PRIORITY -#undef PRIORITY_ABSOLUTE -#undef PRIORITY_HIGH -#undef PRIORITY_NORMAL -#undef PRIORITY_LOW +/datum/client_colour/glass_colour + priority = CLIENT_COLOR_GLASSES_PRIORITY + +/datum/client_colour/glass_colour/green + color = "#aaffaa" + +/datum/client_colour/glass_colour/lightgreen + color = "#ccffcc" + +/datum/client_colour/glass_colour/blue + color = "#aaaaff" + +/datum/client_colour/glass_colour/lightblue + color = "#ccccff" + +/datum/client_colour/glass_colour/yellow + color = "#ffff66" + +/datum/client_colour/glass_colour/lightyellow + color = "#ffffaa" + +/datum/client_colour/glass_colour/red + color = "#ffaaaa" + +/datum/client_colour/glass_colour/lightred + color = "#ffcccc" + +/datum/client_colour/glass_colour/darkred + color = "#bb5555" + +/datum/client_colour/glass_colour/orange + color = "#ffbb99" + +/datum/client_colour/glass_colour/lightorange + color = "#ffddaa" + +/datum/client_colour/glass_colour/purple + color = "#ff99ff" + +/datum/client_colour/glass_colour/lightpurple + color = "#ffccff" + +/datum/client_colour/glass_colour/gray + color = "#cccccc" + +/datum/client_colour/glass_colour/nightmare + color = list(/*R*/ 255,0,0,0, /*G*/ 0,0,0,0, /*B*/ 0,0,0,0, /*A*/ 0,0,0,1, /*C*/ -130,0,0,0) //every color is either red or black + split_filters = TRUE + +#undef CLIENT_COLOR_VALUE_INDEX +#undef CLIENT_COLOR_PRIORITY_INDEX diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 811c6f2bc34..46858f57341 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -30,7 +30,7 @@ /obj/item/clothing/glasses/Initialize(mapload) . = ..() if(glass_colour_type) - AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, forced = forced_glass_color) + AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, GLASSES_TRAIT, forced = forced_glass_color) /obj/item/clothing/glasses/suicide_act(mob/living/carbon/user) user.visible_message(span_suicide("[user] is stabbing \the [src] into [user.p_their()] eyes! It looks like [user.p_theyre()] trying to commit suicide!")) @@ -64,10 +64,10 @@ /obj/item/clothing/glasses/proc/change_glass_color(new_color_type) if(glass_colour_type) - RemoveElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, forced = forced_glass_color) + RemoveElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, GLASSES_TRAIT, forced = forced_glass_color) glass_colour_type = new_color_type if(glass_colour_type) - AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, forced = forced_glass_color) + AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, GLASSES_TRAIT, forced = forced_glass_color) /obj/item/clothing/glasses/meson name = "optical meson scanner" @@ -442,7 +442,7 @@ /obj/item/clothing/glasses/sunglasses/noir name = "noir glasses" desc = "A pair of sleek, futuristic glasses that allow the wearer to see the world in a different light." - glass_colour_type = /datum/client_colour/monochrome + glass_colour_type = /datum/client_colour/monochrome/glasses forced_glass_color = TRUE ///Syndicate item that upgrades the flash protection of your eyes. diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 3b74106d436..a5833dc78f7 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -251,9 +251,9 @@ if(isnull(.)) return if(new_value) - AddElement(/datum/element/wearable_client_colour, /datum/client_colour/halloween_helmet, ITEM_SLOT_HEAD, forced = TRUE) + AddElement(/datum/element/wearable_client_colour, /datum/client_colour/halloween_helmet, ITEM_SLOT_HEAD, HELMET_TRAIT, forced = TRUE) else - RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/halloween_helmet, ITEM_SLOT_HEAD, forced = TRUE) + RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/halloween_helmet, ITEM_SLOT_HEAD, HELMET_TRAIT, forced = TRUE) update_icon(UPDATE_OVERLAYS) /obj/item/clothing/head/utility/hardhat/pumpkinhead/update_overlays() diff --git a/code/modules/clothing/head/perceptomatrix.dm b/code/modules/clothing/head/perceptomatrix.dm index f3be4abd459..ed45ce0f9e3 100644 --- a/code/modules/clothing/head/perceptomatrix.dm +++ b/code/modules/clothing/head/perceptomatrix.dm @@ -64,7 +64,6 @@ /obj/item/clothing/head/helmet/perceptomatrix/Initialize(mapload) . = ..() - update_appearance(UPDATE_ICON_STATE) update_anomaly_state() AddComponent(/datum/component/adjust_fishing_difficulty, -7) // PSYCHIC FISHING @@ -74,9 +73,11 @@ . = ..() if(slot & ITEM_SLOT_HEAD) RegisterSignal(user, COMSIG_MOB_BEFORE_SPELL_CAST, PROC_REF(pre_cast_core_check)) + user.update_sight() /obj/item/clothing/head/helmet/perceptomatrix/dropped(mob/living/user, silent) UnregisterSignal(user, COMSIG_MOB_BEFORE_SPELL_CAST) + user.update_sight() ..() // Prevent casting the spell w/o the core. @@ -93,7 +94,7 @@ clothing_flags = PERCEPTOMATRIX_INACTIVE_FLAGS detach_clothing_traits(additional_clothing_traits) QDEL_LIST(active_components) - RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, forced = TRUE) + RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, HELMET_TRAIT, forced = TRUE) return clothing_flags = PERCEPTOMATRIX_ACTIVE_FLAGS @@ -107,7 +108,7 @@ antimagic_flags = MAGIC_RESISTANCE_MIND, \ inventory_flags = ITEM_SLOT_HEAD, \ ) - AddElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, forced = TRUE) + AddElement(/datum/element/wearable_client_colour, /datum/client_colour/perceptomatrix, ITEM_SLOT_HEAD, HELMET_TRAIT, forced = TRUE) update_icon_state() diff --git a/code/modules/clothing/suits/costume.dm b/code/modules/clothing/suits/costume.dm index 3ef1b1691ca..2b35592f06d 100644 --- a/code/modules/clothing/suits/costume.dm +++ b/code/modules/clothing/suits/costume.dm @@ -23,7 +23,7 @@ /obj/item/clothing/head/hooded/flashsuit/Initialize(mapload) . = ..() - AddElement(/datum/element/wearable_client_colour, /datum/client_colour/flash_hood, ITEM_SLOT_HEAD, forced = TRUE) + AddElement(/datum/element/wearable_client_colour, /datum/client_colour/flash_hood, ITEM_SLOT_HEAD, HELMET_TRAIT, forced = TRUE) /obj/item/clothing/suit/costume/pirate name = "pirate coat" diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm b/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm index c77964cfc44..c16c58dc282 100644 --- a/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm +++ b/code/modules/mining/lavaland/mining_loot/megafauna/bubblegum.dm @@ -121,7 +121,7 @@ var/health_consumed = butchered.maxHealth * 0.1 user.heal_ordered_damage(health_consumed, list(BRUTE, BURN, TOX)) to_chat(user, span_notice("You heal from the corpse of [butchered].")) - var/datum/client_colour/color_effect = user.add_client_colour(/datum/client_colour/bloodlust) + var/datum/client_colour/color_effect = user.add_client_colour(/datum/client_colour/bloodlust, HELMET_TRAIT) QDEL_IN(color_effect, 1 SECONDS) // Soulscythe diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 7ea6966b84d..2fb966cd061 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -184,7 +184,10 @@ /// On [/mob] so clientless mobs will throw alerts properly. var/list/alerts = list() var/list/screens = list() + /// Assoc list of client_colour datum -> source it came from var/list/client_colours = list() + /// List of filter names used in the past client color update for cleanup + var/list/color_filter_store = list() var/hud_type = /datum/hud var/datum/focus //What receives our keyboard inputs. src by default diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index f1b9154358b..989481af76c 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -135,8 +135,8 @@ return ..() /obj/item/mod/module/visor/rave/on_activation() - rave_screen = mod.wearer.add_client_colour(/datum/client_colour/rave) - rave_screen.update_colour(rainbow_order[rave_number]) + rave_screen = mod.wearer.add_client_colour(/datum/client_colour/rave, REF(src)) + rave_screen.update_color(rainbow_order[rave_number]) music_player.start_music(mod.wearer) /obj/item/mod/module/visor/rave/on_deactivation(display_message = TRUE, deleting = FALSE) @@ -161,7 +161,7 @@ if(rave_number > length(rainbow_order)) rave_number = 1 mod.wearer.update_clothing(mod.slot_flags) - rave_screen.update_colour(rainbow_order[rave_number]) + rave_screen.update_color(rainbow_order[rave_number]) /obj/item/mod/module/visor/rave/get_configuration() . = ..() diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm index 5f92ec2cb55..91f13560f2f 100644 --- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm +++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm @@ -44,7 +44,7 @@ Slimecrossing Armor /obj/item/clothing/glasses/prism_glasses/Initialize(mapload) . = ..() - AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, glasses_color, forced_glass_color) + AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, GLASSES_TRAIT, glasses_color, forced_glass_color) /obj/structure/light_prism name = "light prism" @@ -76,9 +76,9 @@ Slimecrossing Armor var/new_color = input(owner, "Choose the lens color:", "Color change",glasses.glasses_color) as color|null if(!new_color) return - RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, glasses.glasses_color, glasses.forced_glass_color) + RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, GLASSES_TRAIT, glasses.glasses_color, glasses.forced_glass_color) glasses.glasses_color = new_color - AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, new_color, glasses.forced_glass_color) + AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, GLASSES_TRAIT, new_color, glasses.forced_glass_color) /datum/action/item_action/place_light_prism name = "Fabricate Light Prism" diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm index 61899a8dbec..d5107e9a402 100644 --- a/code/modules/surgery/bodyparts/robot_bodyparts.dm +++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm @@ -404,10 +404,7 @@ if (severity == EMP_HEAVY) glitch_duration *= 2 - owner.add_client_colour(/datum/client_colour/malfunction) - - addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob/living/carbon/human, remove_client_colour), /datum/client_colour/malfunction), glitch_duration) - return + QDEL_IN(owner.add_client_colour(/datum/client_colour/malfunction, HEAD_TRAIT), glitch_duration) #undef EMP_GLITCH diff --git a/code/modules/unit_tests/blindness.dm b/code/modules/unit_tests/blindness.dm index e4e6a4667fd..6ab6058aa61 100644 --- a/code/modules/unit_tests/blindness.dm +++ b/code/modules/unit_tests/blindness.dm @@ -52,14 +52,14 @@ // Check for the status effect, duh TEST_ASSERT(dummy.is_blind(), "Dummy, [status_message], did not have the blind status effect.") // Being more technical, we need to check for client color and screen overlays - TEST_ASSERT(HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome/blind), "Dummy, [status_message], did not have the monochrome client color.") + TEST_ASSERT(HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome), "Dummy, [status_message], did not have the monochrome client color.") TEST_ASSERT(HAS_SCREEN_OVERLAY(dummy, /atom/movable/screen/fullscreen/blind), "Dummy, [status_message], did not have a blind screen overlay in their list of screens.") /datum/unit_test/blindness/proc/check_if_not_blind(mob/living/carbon/human/dummy, status_message = "after being cured of blindness") // Check for no status effect TEST_ASSERT(!dummy.is_blind(), "Dummy, [status_message], still had the blindness status effect.") // Check that the client color and screen overlay are gone - TEST_ASSERT(!HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome/blind), "Dummy, [status_message], still had the monochrome client color.") + TEST_ASSERT(!HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome), "Dummy, [status_message], still had the monochrome client color.") TEST_ASSERT(!HAS_SCREEN_OVERLAY(dummy, /atom/movable/screen/fullscreen/blind), "Dummy, [status_message], still had the blind sceen overlay.") /** diff --git a/code/modules/unit_tests/client_colours.dm b/code/modules/unit_tests/client_colours.dm index 55b6e0b24ad..7275394eb4f 100644 --- a/code/modules/unit_tests/client_colours.dm +++ b/code/modules/unit_tests/client_colours.dm @@ -5,5 +5,11 @@ for(var/datum/client_colour/colour as anything in subtypesof(/datum/client_colour)) // colours can be color matrices (lists), which initial() cannot read. colour = new colour - if(!color_to_full_rgba_matrix(colour.colour, FALSE)) - TEST_FAIL("[colour.type] has an invalid default colour value: [colour.colour]") + if (islist(colour.color)) + var/list/potential_filter = colour.color + // If our list has "type" in it then its a filter, ignore that + if (potential_filter["type"]) + continue + + if(!color_to_full_rgba_matrix(colour.color, FALSE)) + TEST_FAIL("[colour.type] has an invalid default colour value: [colour.color]")