ColorFlashEffectSystem.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. using Content.Shared.Effects;
  2. using Robust.Client.Animations;
  3. using Robust.Client.GameObjects;
  4. using Robust.Shared.Animations;
  5. using Robust.Shared.Collections;
  6. using Robust.Shared.Player;
  7. using Robust.Shared.Timing;
  8. using Robust.Shared.Utility;
  9. namespace Content.Client.Effects;
  10. public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
  11. {
  12. [Dependency] private readonly IGameTiming _timing = default!;
  13. [Dependency] private readonly AnimationPlayerSystem _animation = default!;
  14. /// <summary>
  15. /// It's a little on the long side but given we use multiple colours denoting what happened it makes it easier to register.
  16. /// </summary>
  17. private const float AnimationLength = 0.30f;
  18. private const string AnimationKey = "color-flash-effect";
  19. private ValueList<EntityUid> _toRemove = new();
  20. public override void Initialize()
  21. {
  22. base.Initialize();
  23. SubscribeAllEvent<ColorFlashEffectEvent>(OnColorFlashEffect);
  24. SubscribeLocalEvent<ColorFlashEffectComponent, AnimationCompletedEvent>(OnEffectAnimationCompleted);
  25. }
  26. public override void RaiseEffect(Color color, List<EntityUid> entities, Filter filter)
  27. {
  28. if (!_timing.IsFirstTimePredicted)
  29. return;
  30. OnColorFlashEffect(new ColorFlashEffectEvent(color, GetNetEntityList(entities)));
  31. }
  32. private void OnEffectAnimationCompleted(EntityUid uid, ColorFlashEffectComponent component, AnimationCompletedEvent args)
  33. {
  34. if (args.Key != AnimationKey)
  35. return;
  36. if (TryComp<SpriteComponent>(uid, out var sprite))
  37. {
  38. sprite.Color = component.Color;
  39. }
  40. }
  41. public override void Update(float frameTime)
  42. {
  43. base.Update(frameTime);
  44. var query = AllEntityQuery<ColorFlashEffectComponent>();
  45. _toRemove.Clear();
  46. // Can't use deferred removal on animation completion or it will cause issues.
  47. while (query.MoveNext(out var uid, out _))
  48. {
  49. if (_animation.HasRunningAnimation(uid, AnimationKey))
  50. continue;
  51. _toRemove.Add(uid);
  52. }
  53. foreach (var ent in _toRemove)
  54. {
  55. RemComp<ColorFlashEffectComponent>(ent);
  56. }
  57. }
  58. private Animation? GetDamageAnimation(EntityUid uid, Color color, SpriteComponent? sprite = null)
  59. {
  60. if (!Resolve(uid, ref sprite, false))
  61. return null;
  62. // 90% of them are going to be this so why allocate a new class.
  63. return new Animation
  64. {
  65. Length = TimeSpan.FromSeconds(AnimationLength),
  66. AnimationTracks =
  67. {
  68. new AnimationTrackComponentProperty
  69. {
  70. ComponentType = typeof(SpriteComponent),
  71. Property = nameof(SpriteComponent.Color),
  72. InterpolationMode = AnimationInterpolationMode.Linear,
  73. KeyFrames =
  74. {
  75. new AnimationTrackProperty.KeyFrame(color, 0f),
  76. new AnimationTrackProperty.KeyFrame(sprite.Color, AnimationLength)
  77. }
  78. }
  79. }
  80. };
  81. }
  82. private void OnColorFlashEffect(ColorFlashEffectEvent ev)
  83. {
  84. var color = ev.Color;
  85. foreach (var nent in ev.Entities)
  86. {
  87. var ent = GetEntity(nent);
  88. if (Deleted(ent) || !TryComp(ent, out SpriteComponent? sprite))
  89. {
  90. continue;
  91. }
  92. if (!TryComp(ent, out ColorFlashEffectComponent? comp))
  93. {
  94. #if DEBUG
  95. DebugTools.Assert(!_animation.HasRunningAnimation(ent, AnimationKey));
  96. #endif
  97. }
  98. _animation.Stop(ent, AnimationKey);
  99. var animation = GetDamageAnimation(ent, color, sprite);
  100. if (animation == null)
  101. {
  102. continue;
  103. }
  104. var targetEv = new GetFlashEffectTargetEvent(ent);
  105. RaiseLocalEvent(ent, ref targetEv);
  106. ent = targetEv.Target;
  107. EnsureComp<ColorFlashEffectComponent>(ent, out comp);
  108. comp.NetSyncEnabled = false;
  109. comp.Color = sprite.Color;
  110. _animation.Play(ent, animation, AnimationKey);
  111. }
  112. }
  113. }
  114. /// <summary>
  115. /// Raised on an entity to change the target for a color flash effect.
  116. /// </summary>
  117. [ByRefEvent]
  118. public record struct GetFlashEffectTargetEvent(EntityUid Target);