using Content.Shared.Effects; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Animations; using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Client.Effects; public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AnimationPlayerSystem _animation = default!; /// /// It's a little on the long side but given we use multiple colours denoting what happened it makes it easier to register. /// private const float AnimationLength = 0.30f; private const string AnimationKey = "color-flash-effect"; public override void Initialize() { base.Initialize(); SubscribeAllEvent(OnColorFlashEffect); SubscribeLocalEvent(OnEffectAnimationCompleted); } public override void RaiseEffect(Color color, List entities, Filter filter) { if (!_timing.IsFirstTimePredicted) return; OnColorFlashEffect(new ColorFlashEffectEvent(color, GetNetEntityList(entities))); } private void OnEffectAnimationCompleted(EntityUid uid, ColorFlashEffectComponent component, AnimationCompletedEvent args) { if (args.Key != AnimationKey) return; if (TryComp(uid, out var sprite)) { sprite.Color = component.Color; } RemCompDeferred(uid); } private Animation? GetDamageAnimation(EntityUid uid, Color color, SpriteComponent? sprite = null) { if (!Resolve(uid, ref sprite, false)) return null; // 90% of them are going to be this so why allocate a new class. return new Animation { Length = TimeSpan.FromSeconds(AnimationLength), AnimationTracks = { new AnimationTrackComponentProperty { ComponentType = typeof(SpriteComponent), Property = nameof(SpriteComponent.Color), InterpolationMode = AnimationInterpolationMode.Linear, KeyFrames = { new AnimationTrackProperty.KeyFrame(color, 0f), new AnimationTrackProperty.KeyFrame(sprite.Color, AnimationLength) } } } }; } private void OnColorFlashEffect(ColorFlashEffectEvent ev) { var color = ev.Color; foreach (var nent in ev.Entities) { var ent = GetEntity(nent); if (Deleted(ent)) { continue; } var player = EnsureComp(ent); player.NetSyncEnabled = false; // Need to stop the existing animation first to ensure the sprite color is fixed. // Otherwise we might lerp to a red colour instead. if (_animation.HasRunningAnimation(ent, player, AnimationKey)) { _animation.Stop(ent, player, AnimationKey); } if (!TryComp(ent, out var sprite)) { continue; } if (TryComp(ent, out var effect)) { sprite.Color = effect.Color; } var animation = GetDamageAnimation(ent, color, sprite); if (animation == null) continue; var comp = EnsureComp(ent); comp.NetSyncEnabled = false; comp.Color = sprite.Color; _animation.Play(player, animation, AnimationKey); } } }