Files
SmArtKarandGitHub 845ed7459e 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.

<details>

<summary>Example of how Perceptomatrix and nightmare vision goggles
combined before this PR:</summary>


![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)

</details>

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
🆑
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.
/🆑
2025-03-17 11:35:11 +00:00

116 lines
3.7 KiB
Plaintext

/// Helper macro, for ease of expanding checks for mobs which cannot be blinded
/// There are no reason why these cannot be blinded, it is simply for "design reasons" (these things shouldn't be blinded)
#define CAN_BE_BLIND(mob) (!isanimal_or_basicmob(mob) && !isbrain(mob) && !isrevenant(mob))
/// 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
var/static/list/update_signals = list(
SIGNAL_REMOVETRAIT(TRAIT_SIGHT_BYPASS),
SIGNAL_ADDTRAIT(TRAIT_SIGHT_BYPASS),
)
/// 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))
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
qdel(src)
return
if (!HAS_TRAIT(owner, TRAIT_SIGHT_BYPASS))
make_blind()
return
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, REF(src))
/datum/status_effect/grouped/blindness/proc/make_unblind()
owner.clear_fullscreen(id)
owner.remove_client_colour(REF(src))
/datum/status_effect/grouped/blindness/on_remove()
make_unblind()
UnregisterSignal(owner, update_signals)
return ..()
/atom/movable/screen/alert/status_effect/blind
name = "Blind"
desc = "You can't see! This may be caused by a genetic defect, eye trauma, being unconscious, or something covering your eyes."
icon_state = "blind"
/// This status effect handles applying a temporary blind to the mob.
/datum/status_effect/temporary_blindness
id = "temporary_blindness"
tick_interval = 2 SECONDS
alert_type = null
remove_on_fullheal = TRUE
/datum/status_effect/temporary_blindness/on_creation(mob/living/new_owner, duration = 10 SECONDS)
src.duration = duration
return ..()
/datum/status_effect/temporary_blindness/on_apply()
if(!CAN_BE_BLIND(owner))
return FALSE
owner.become_blind(id)
return TRUE
/datum/status_effect/temporary_blindness/on_remove()
owner.cure_blind(id)
/datum/status_effect/temporary_blindness/tick(seconds_between_ticks)
if(owner.stat == DEAD)
return
// Temp. blindness heals faster if our eyes are covered
if(!owner.is_blind_from(EYES_COVERED))
return
// Knocks 2 seconds off of our duration
// If we should be deleted, give a message letting them know
var/mob/living/stored_owner = owner
if(remove_duration(2 SECONDS))
to_chat(stored_owner, span_green("Your eyes start to feel better!"))
return
// Otherwise add a chance to let them know that it's working
else if(SPT_PROB(5, seconds_between_ticks))
var/obj/item/thing_covering_eyes = owner.is_eyes_covered()
// "Your blindfold soothes your eyes", for example
to_chat(owner, span_green("Your [thing_covering_eyes?.name || "eye covering"] soothes your eyes."))
#undef CAN_BE_BLIND