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