From 3400af11cdddc323c349af079e26dbdf600070f4 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 5 Jan 2023 16:45:23 +1100 Subject: [PATCH] Add sprite fades for occluding sprites (#13267) --- .../Sprite/FadingSpriteComponent.cs | 13 +++ Content.Client/Sprite/SpriteFadeSystem.cs | 102 ++++++++++++++++++ Content.Shared/Sprite/SpriteFadeComponent.cs | 12 +++ .../Entities/Objects/Decoration/flora.yml | 1 + 4 files changed, 128 insertions(+) create mode 100644 Content.Client/Sprite/FadingSpriteComponent.cs create mode 100644 Content.Client/Sprite/SpriteFadeSystem.cs create mode 100644 Content.Shared/Sprite/SpriteFadeComponent.cs diff --git a/Content.Client/Sprite/FadingSpriteComponent.cs b/Content.Client/Sprite/FadingSpriteComponent.cs new file mode 100644 index 0000000000..ea211df2bb --- /dev/null +++ b/Content.Client/Sprite/FadingSpriteComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Sprite; + +namespace Content.Client.Sprite; + +/// +/// The non-networked client-only component to track active +/// +[RegisterComponent, Access(typeof(SpriteFadeSystem))] +public sealed class FadingSpriteComponent : Component +{ + [ViewVariables] + public float OriginalAlpha; +} diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs new file mode 100644 index 0000000000..f6a94e6db1 --- /dev/null +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -0,0 +1,102 @@ +using Content.Client.Gameplay; +using Content.Shared.Sprite; +using Robust.Client.GameObjects; +using Robust.Client.Player; +using Robust.Client.State; + +namespace Content.Client.Sprite; + +public sealed class SpriteFadeSystem : EntitySystem +{ + /* + * If the player entity is obstructed under the specified components then it will drop the alpha for that entity + * so the player is still visible. + */ + + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + + private readonly HashSet _comps = new(); + + private const float TargetAlpha = 0.4f; + private const float ChangeRate = 1f; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnFadingShutdown); + } + + private void OnFadingShutdown(EntityUid uid, FadingSpriteComponent component, ComponentShutdown args) + { + if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating || !TryComp(uid, out var sprite)) + return; + + sprite.Color = sprite.Color.WithAlpha(component.OriginalAlpha); + } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + + var player = _playerManager.LocalPlayer?.ControlledEntity; + var spriteQuery = GetEntityQuery(); + var change = ChangeRate * frameTime; + + if (TryComp(player, out var playerXform) && + _stateManager.CurrentState is GameplayState state && + spriteQuery.TryGetComponent(player, out var playerSprite)) + { + var fadeQuery = GetEntityQuery(); + var mapPos = playerXform.MapPosition; + + // Also want to handle large entities even if they may not be clickable. + foreach (var ent in state.GetClickableEntities(mapPos)) + { + if (ent == player || + !fadeQuery.HasComponent(ent) || + !spriteQuery.TryGetComponent(ent, out var sprite) || + sprite.DrawDepth < playerSprite.DrawDepth) + { + continue; + } + + if (!TryComp(ent, out var fading)) + { + fading = AddComp(ent); + fading.OriginalAlpha = sprite.Color.A; + } + + _comps.Add(fading); + var newColor = Math.Max(sprite.Color.A - change, TargetAlpha); + + if (!sprite.Color.A.Equals(newColor)) + { + sprite.Color = sprite.Color.WithAlpha(newColor); + } + } + } + + foreach (var comp in EntityQuery(true)) + { + if (_comps.Contains(comp)) + continue; + + if (!spriteQuery.TryGetComponent(comp.Owner, out var sprite)) + continue; + + var newColor = Math.Min(sprite.Color.A + change, comp.OriginalAlpha); + + if (!newColor.Equals(sprite.Color.A)) + { + sprite.Color = sprite.Color.WithAlpha(newColor); + } + else + { + RemCompDeferred(comp.Owner); + } + } + + _comps.Clear(); + } +} diff --git a/Content.Shared/Sprite/SpriteFadeComponent.cs b/Content.Shared/Sprite/SpriteFadeComponent.cs new file mode 100644 index 0000000000..3e390b4c1f --- /dev/null +++ b/Content.Shared/Sprite/SpriteFadeComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Sprite; + +/// +/// If your client entity is behind this then the sprite's alpha will be lowered so your entity remains visible. +/// +[RegisterComponent, NetworkedComponent] +public sealed class SpriteFadeComponent : Component +{ + +} diff --git a/Resources/Prototypes/Entities/Objects/Decoration/flora.yml b/Resources/Prototypes/Entities/Objects/Decoration/flora.yml index 72e31e3830..61e50146e4 100644 --- a/Resources/Prototypes/Entities/Objects/Decoration/flora.yml +++ b/Resources/Prototypes/Entities/Objects/Decoration/flora.yml @@ -37,6 +37,7 @@ description: Yep, it's a tree. abstract: true components: + - type: SpriteFade - type: Clickable - type: Sprite sprite: Objects/Decoration/Flora/flora_trees.rsi