DoAfterOverlay.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. using System.Numerics;
  2. using Content.Shared.DoAfter;
  3. using Content.Client.UserInterface.Systems;
  4. using Robust.Client.GameObjects;
  5. using Robust.Client.Graphics;
  6. using Robust.Shared.Enums;
  7. using Robust.Client.Player;
  8. using Robust.Shared.Prototypes;
  9. using Robust.Shared.Timing;
  10. using Robust.Shared.Utility;
  11. using Robust.Shared.Containers;
  12. namespace Content.Client.DoAfter;
  13. public sealed class DoAfterOverlay : Overlay
  14. {
  15. private readonly IEntityManager _entManager;
  16. private readonly IGameTiming _timing;
  17. private readonly IPlayerManager _player;
  18. private readonly SharedTransformSystem _transform;
  19. private readonly MetaDataSystem _meta;
  20. private readonly ProgressColorSystem _progressColor;
  21. private readonly SharedContainerSystem _container;
  22. private readonly Texture _barTexture;
  23. private readonly ShaderInstance _unshadedShader;
  24. /// <summary>
  25. /// Flash time for cancelled DoAfters
  26. /// </summary>
  27. private const float FlashTime = 0.125f;
  28. // Hardcoded width of the progress bar because it doesn't match the texture.
  29. private const float StartX = 2;
  30. private const float EndX = 22f;
  31. public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
  32. public DoAfterOverlay(IEntityManager entManager, IPrototypeManager protoManager, IGameTiming timing, IPlayerManager player)
  33. {
  34. _entManager = entManager;
  35. _timing = timing;
  36. _player = player;
  37. _transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
  38. _meta = _entManager.EntitySysManager.GetEntitySystem<MetaDataSystem>();
  39. _container = _entManager.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
  40. _progressColor = _entManager.System<ProgressColorSystem>();
  41. var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
  42. _barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
  43. _unshadedShader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
  44. }
  45. protected override void Draw(in OverlayDrawArgs args)
  46. {
  47. var handle = args.WorldHandle;
  48. var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero;
  49. var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
  50. // If you use the display UI scale then need to set max(1f, displayscale) because 0 is valid.
  51. const float scale = 1f;
  52. var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
  53. var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
  54. var curTime = _timing.CurTime;
  55. var bounds = args.WorldAABB.Enlarged(5f);
  56. var localEnt = _player.LocalSession?.AttachedEntity;
  57. var metaQuery = _entManager.GetEntityQuery<MetaDataComponent>();
  58. var enumerator = _entManager.AllEntityQueryEnumerator<ActiveDoAfterComponent, DoAfterComponent, SpriteComponent, TransformComponent>();
  59. while (enumerator.MoveNext(out var uid, out _, out var comp, out var sprite, out var xform))
  60. {
  61. if (xform.MapID != args.MapId)
  62. continue;
  63. if (comp.DoAfters.Count == 0)
  64. continue;
  65. var worldPosition = _transform.GetWorldPosition(xform, xformQuery);
  66. if (!bounds.Contains(worldPosition))
  67. continue;
  68. // shades the do-after bar if the do-after bar belongs to other players
  69. // does not shade do-afters belonging to the local player
  70. if (uid != localEnt)
  71. handle.UseShader(null);
  72. else
  73. handle.UseShader(_unshadedShader);
  74. // If the entity is paused, we will draw the do-after as it was when the entity got paused.
  75. var meta = metaQuery.GetComponent(uid);
  76. var time = meta.EntityPaused
  77. ? curTime - _meta.GetPauseTime(uid, meta)
  78. : curTime;
  79. var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition);
  80. var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
  81. var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
  82. handle.SetTransform(matty);
  83. var offset = 0f;
  84. var isInContainer = _container.IsEntityOrParentInContainer(uid, meta, xform);
  85. foreach (var doAfter in comp.DoAfters.Values)
  86. {
  87. // Hide some DoAfters from other players for stealthy actions (ie: thieving gloves)
  88. var alpha = 1f;
  89. if (doAfter.Args.Hidden || isInContainer)
  90. {
  91. if (uid != localEnt)
  92. continue;
  93. // Hints to the local player that this do-after is not visible to other players.
  94. alpha = 0.5f;
  95. }
  96. // Use the sprite itself if we know its bounds. This means short or tall sprites don't get overlapped
  97. // by the bar.
  98. float yOffset = sprite.Bounds.Height / 2f + 0.05f;
  99. // Position above the entity (we've already applied the matrix transform to the entity itself)
  100. // Offset by the texture size for every do_after we have.
  101. var position = new Vector2(-_barTexture.Width / 2f / EyeManager.PixelsPerMeter,
  102. yOffset / scale + offset / EyeManager.PixelsPerMeter * scale);
  103. // Draw the underlying bar texture
  104. handle.DrawTexture(_barTexture, position);
  105. Color color;
  106. float elapsedRatio;
  107. // if we're cancelled then flick red / off.
  108. if (doAfter.CancelledTime != null)
  109. {
  110. var elapsed = doAfter.CancelledTime.Value - doAfter.StartTime;
  111. elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
  112. var cancelElapsed = (time - doAfter.CancelledTime.Value).TotalSeconds;
  113. var flash = Math.Floor(cancelElapsed / FlashTime) % 2 == 0;
  114. color = GetProgressColor(0, flash ? alpha : 0);
  115. }
  116. else
  117. {
  118. var elapsed = time - doAfter.StartTime;
  119. elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
  120. color = GetProgressColor(elapsedRatio, alpha);
  121. }
  122. var xProgress = (EndX - StartX) * elapsedRatio + StartX;
  123. var box = new Box2(new Vector2(StartX, 3f) / EyeManager.PixelsPerMeter, new Vector2(xProgress, 4f) / EyeManager.PixelsPerMeter);
  124. box = box.Translated(position);
  125. handle.DrawRect(box, color);
  126. offset += _barTexture.Height / scale;
  127. }
  128. }
  129. handle.UseShader(null);
  130. handle.SetTransform(Matrix3x2.Identity);
  131. }
  132. public Color GetProgressColor(float progress, float alpha = 1f)
  133. {
  134. return _progressColor.GetProgressColor(progress).WithAlpha(alpha);
  135. }
  136. }