CatStation/Content.Client/Wall/WallMountFadeState.cs
B_Kirill 6938064aa5
Directional visibility for wall-mounted entities 2 (#44801)
* Directional visibility for wall-mounted entities (#43476)

* Directional visibility for wall-mounted entities

* Fix cache

* Revert

* InteractionIgnoreAnchoredInTileComponent + yml

* Main system

* Move it

* Directional arrow

* Add fade

* retry

* Split "Directional arrow"

* fix

* small thing

* Split "InteractionIgnoreAnchoredInTileComponent"

* correct description

* I hate the online editor

* retry

* ...

* Apply suggestions from code review

Co-authored-by: Jessica M <jessica@jessicamaybe.com>

* Additional comment

* cleanup

---------

Co-authored-by: Jessica M <jessica@jessicamaybe.com>
Co-authored-by: AJCM-git <ajcm_mail@disroot.org>

* 2

* Kinda useless

* Fade CVar

* Fix CVar runtime change

* Clear _originalAlphas on dispose

* Boring DI

* retry

* WallComponent

---------

Co-authored-by: Jessica M <jessica@jessicamaybe.com>
Co-authored-by: AJCM-git <ajcm_mail@disroot.org>
2026-07-30 19:40:53 +00:00

99 lines
2.8 KiB
C#

using Robust.Client.GameObjects;
namespace Content.Client.Wall;
/// <summary>
/// Tracks fade progress for a wall-mounted entity.
/// </summary>
public struct WallMountFadeState
{
/// <summary>
/// The sprite's original alpha before any fade was applied.
/// </summary>
public float OriginalAlpha;
/// <summary>
/// The current fade multiplier.
/// </summary>
public float CurrentAlpha;
/// <summary>
/// The target fade multiplier.
/// </summary>
public float TargetAlpha;
/// <summary>
/// Sprite alpha after applying the fade multiplier.
/// </summary>
public readonly float EffectiveAlpha => OriginalAlpha * CurrentAlpha;
/// <summary>
/// Whether the entity should be rendered as visible at the current fade level.
/// </summary>
public readonly bool IsVisible => CurrentAlpha > 0f;
/// <summary>
/// Creates a new fade state snapped immediately to the given target alpha.
/// </summary>
public static WallMountFadeState Snapped(float originalAlpha, float targetAlpha)
{
return new()
{
OriginalAlpha = originalAlpha,
CurrentAlpha = targetAlpha,
TargetAlpha = targetAlpha,
};
}
/// <summary>
/// Moves <see cref="CurrentAlpha"/> one step towards <see cref="TargetAlpha"/>.
/// </summary>
public void StepTowards(float fadeStep)
{
if (MathHelper.CloseTo(CurrentAlpha, TargetAlpha))
return;
var delta = TargetAlpha - CurrentAlpha;
var step = MathF.Sign(delta) * MathF.Min(MathF.Abs(delta), fadeStep);
CurrentAlpha = Math.Clamp(CurrentAlpha + step, 0f, 1f);
}
}
/// <summary>
/// Per-viewport fade state for wall-mounted entities.
/// </summary>
public sealed class ViewportFadeState(SpriteSystem sprite, EntityQuery<SpriteComponent> spriteQuery) : IDisposable
{
private readonly SpriteSystem _sprite = sprite;
private readonly EntityQuery<SpriteComponent> _spriteQuery = spriteQuery;
/// <summary>
/// Fade states for entities tracked in this viewport.
/// </summary>
public readonly Dictionary<EntityUid, WallMountFadeState> FadeStates = [];
/// <summary>
/// Entities visible this frame.
/// </summary>
public readonly HashSet<EntityUid> SeenThisFrame = [];
/// <summary>
/// Whether FOV was enabled during the previous frame.
/// </summary>
public bool WasFovEnabled = true;
public void Dispose()
{
foreach (var (uid, state) in FadeStates)
{
if (!_spriteQuery.TryGetComponent(uid, out var sprite))
continue;
_sprite.SetColor((uid, sprite), sprite.Color.WithAlpha(state.OriginalAlpha));
_sprite.SetVisible((uid, sprite), true);
}
FadeStates.Clear();
SeenThisFrame.Clear();
}
}