using UnityEngine; namespace NecromancerTome { /// /// Drains the colour out of the world while the necromancer is channelling something, and /// lets it back in when he stops (user request 2026-09-14: "эффект, когда мир становится /// тёмным и чёрнобелым... повесить его на момент ожидания применения порталов и на момент /// ожидания утаскивания блока", with "желательно плавно... секунды за 3" in both directions). /// Shared by both channels so the look, the timing and the name of the effect live in one /// place rather than drifting apart in two files. /// /// IT IS THE GAME'S OWN POST-PROCESS, not a reimplementation. EntityPlayerLocal carries a /// ScreenEffects component - ScreenEffectManager - whose SetScreenEffect(name, intensity, /// fadeTime) is what the engine itself calls for dying ("Dying"), for spawning in /// ("VibrantDeSat") and for every buff in the game that tints the screen. THE FADE IS THAT /// THIRD ARGUMENT: three seconds in and three seconds out cost nothing to implement, because /// the ramp is the effect system's own. /// /// THE EFFECT IS "Greyscale", AND THE CHOICE IS ABOUT WHO ELSE TOUCHES IT. These effects are /// a flat namespace of materials loaded from Resources/ScreenEffects - anyone writing to a /// name overwrites whatever was there, so picking one is mostly picking a fight to avoid: /// /// - "Greyscale" is written by exactly two things in the whole game, twitch_buffMonochrome /// and sandbox_blackandwhite - a Twitch-integration reward and a game-mode toggle. Neither /// happens in an ordinary session, so the channel owns it in practice. /// - "Dying" and "Dead" are the death visuals the user was describing, and they are exactly /// the ones NOT to borrow: EntityPlayerLocal.Update writes "Dying" from the player's own /// health every time it changes, so any damage mid-channel would take the effect over - /// and being hit mid-channel is a thing that happens. /// - "Dark" would have supplied the darkening half. It belongs to buffCrouching, which /// fires on every crouch with a 0.2s fade and would stamp on this one. /// /// SO THE DARKENING HALF IS DELIBERATELY NOT DONE. Both effects that dim the screen are owned /// by something that fights for them - crouching, and dying - and losing that fight looks like /// a bug in this mod rather than in the effect system. Greyscale alone reads as the world /// going wrong, which is what was actually asked for; if it wants to be darker too, the list /// below takes a second entry and nothing else changes. /// /// NOTHING HERE TOUCHES INPUT. The effect is a camera post-process and outlives the timer /// window on purpose: the three-second fade back keeps running while the player walks away, /// which is the point of asking for a fade rather than a switch. /// public static class ChannelVision { /// Seconds to fade in, and to fade back out. public const float FadeSeconds = 3f; /// What to fade, and how far. A list rather than a single name so a second layer /// is one entry and not a rewrite - see the class comment on the darkening half. public static readonly string[] EffectNames = { "Greyscale" }; /// Full strength per effect, in the same order as EffectNames. public static readonly float[] EffectIntensities = { 1f }; /// Colour drains out over FadeSeconds. public static void Begin(EntityPlayerLocal _player) { Apply(_player, _fullStrength: true); } /// Colour comes back over FadeSeconds. Safe to call when nothing is running - /// fading an effect that is already at zero to zero does nothing - which is what lets /// every exit path call it without first working out whether it is the one that has to. /// public static void End(EntityPlayerLocal _player) { Apply(_player, _fullStrength: false); } public static void Apply(EntityPlayerLocal _player, bool _fullStrength) { if (_player == null || _player.ScreenEffectManager == null) { return; } for (int i = 0; i < EffectNames.Length; i++) { float intensity = _fullStrength ? EffectIntensities[i] : 0f; _player.ScreenEffectManager.SetScreenEffect(EffectNames[i], intensity, FadeSeconds); } } } }