StatusIconOverlay.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using Content.Shared.StatusIcon;
  2. using Content.Shared.StatusIcon.Components;
  3. using Robust.Client.GameObjects;
  4. using Robust.Client.Graphics;
  5. using Robust.Shared.Enums;
  6. using Robust.Shared.Prototypes;
  7. using Robust.Shared.Timing;
  8. using System.Numerics;
  9. namespace Content.Client.StatusIcon;
  10. public sealed class StatusIconOverlay : Overlay
  11. {
  12. [Dependency] private readonly IEntityManager _entity = default!;
  13. [Dependency] private readonly IPrototypeManager _prototype = default!;
  14. [Dependency] private readonly IGameTiming _timing = default!;
  15. private readonly SpriteSystem _sprite;
  16. private readonly TransformSystem _transform;
  17. private readonly StatusIconSystem _statusIcon;
  18. private readonly ShaderInstance _unshadedShader;
  19. public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
  20. internal StatusIconOverlay()
  21. {
  22. IoCManager.InjectDependencies(this);
  23. _sprite = _entity.System<SpriteSystem>();
  24. _transform = _entity.System<TransformSystem>();
  25. _statusIcon = _entity.System<StatusIconSystem>();
  26. _unshadedShader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
  27. }
  28. protected override void Draw(in OverlayDrawArgs args)
  29. {
  30. var handle = args.WorldHandle;
  31. var eyeRot = args.Viewport.Eye?.Rotation ?? default;
  32. var xformQuery = _entity.GetEntityQuery<TransformComponent>();
  33. var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(1, 1));
  34. var rotationMatrix = Matrix3Helpers.CreateRotation(-eyeRot);
  35. var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>();
  36. while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
  37. {
  38. if (xform.MapID != args.MapId || !sprite.Visible)
  39. continue;
  40. var bounds = comp.Bounds ?? sprite.Bounds;
  41. var worldPos = _transform.GetWorldPosition(xform, xformQuery);
  42. if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
  43. continue;
  44. var icons = _statusIcon.GetStatusIcons(uid, meta);
  45. if (icons.Count == 0)
  46. continue;
  47. var worldMatrix = Matrix3Helpers.CreateTranslation(worldPos);
  48. var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
  49. var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
  50. handle.SetTransform(matty);
  51. var countL = 0;
  52. var countR = 0;
  53. var accOffsetL = 0;
  54. var accOffsetR = 0;
  55. icons.Sort();
  56. foreach (var proto in icons)
  57. {
  58. if (!_statusIcon.IsVisible((uid, meta), proto))
  59. continue;
  60. var curTime = _timing.RealTime;
  61. var texture = _sprite.GetFrame(proto.Icon, curTime);
  62. float yOffset;
  63. float xOffset;
  64. // the icons are ordered left to right, top to bottom.
  65. // extra icons that don't fit are just cut off.
  66. if (proto.LocationPreference == StatusIconLocationPreference.Left ||
  67. proto.LocationPreference == StatusIconLocationPreference.None && countL <= countR)
  68. {
  69. if (accOffsetL + texture.Height > sprite.Bounds.Height * EyeManager.PixelsPerMeter)
  70. break;
  71. if (proto.Layer == StatusIconLayer.Base)
  72. {
  73. accOffsetL += texture.Height;
  74. countL++;
  75. }
  76. yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) (accOffsetL - proto.Offset) / EyeManager.PixelsPerMeter;
  77. xOffset = -(bounds.Width + sprite.Offset.X) / 2f;
  78. }
  79. else
  80. {
  81. if (accOffsetR + texture.Height > sprite.Bounds.Height * EyeManager.PixelsPerMeter)
  82. break;
  83. if (proto.Layer == StatusIconLayer.Base)
  84. {
  85. accOffsetR += texture.Height;
  86. countR++;
  87. }
  88. yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float) (accOffsetR - proto.Offset) / EyeManager.PixelsPerMeter;
  89. xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float) texture.Width / EyeManager.PixelsPerMeter;
  90. }
  91. if (proto.IsShaded)
  92. handle.UseShader(null);
  93. else
  94. handle.UseShader(_unshadedShader);
  95. var position = new Vector2(xOffset, yOffset);
  96. handle.DrawTexture(texture, position);
  97. }
  98. handle.UseShader(null);
  99. }
  100. }
  101. }