ItemGridPiece.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. using System.Numerics;
  2. using Content.Client.Items.Systems;
  3. using Content.Shared.Item;
  4. using Content.Shared.Storage;
  5. using Robust.Client.GameObjects;
  6. using Robust.Client.Graphics;
  7. using Robust.Client.UserInterface;
  8. using Robust.Client.UserInterface.CustomControls;
  9. namespace Content.Client.UserInterface.Systems.Storage.Controls;
  10. public sealed class ItemGridPiece : Control, IEntityControl
  11. {
  12. private readonly IEntityManager _entityManager;
  13. private readonly StorageUIController _storageController;
  14. private readonly List<(Texture, Vector2)> _texturesPositions = new();
  15. public readonly EntityUid Entity;
  16. public ItemStorageLocation Location;
  17. public ItemGridPieceMarks? Marked;
  18. public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPiecePressed;
  19. public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPieceUnpressed;
  20. #region Textures
  21. private readonly string _centerTexturePath = "Storage/piece_center";
  22. private Texture? _centerTexture;
  23. private readonly string _topTexturePath = "Storage/piece_top";
  24. private Texture? _topTexture;
  25. private readonly string _bottomTexturePath = "Storage/piece_bottom";
  26. private Texture? _bottomTexture;
  27. private readonly string _leftTexturePath = "Storage/piece_left";
  28. private Texture? _leftTexture;
  29. private readonly string _rightTexturePath = "Storage/piece_right";
  30. private Texture? _rightTexture;
  31. private readonly string _topLeftTexturePath = "Storage/piece_topLeft";
  32. private Texture? _topLeftTexture;
  33. private readonly string _topRightTexturePath = "Storage/piece_topRight";
  34. private Texture? _topRightTexture;
  35. private readonly string _bottomLeftTexturePath = "Storage/piece_bottomLeft";
  36. private Texture? _bottomLeftTexture;
  37. private readonly string _bottomRightTexturePath = "Storage/piece_bottomRight";
  38. private Texture? _bottomRightTexture;
  39. private readonly string _markedFirstTexturePath = "Storage/marked_first";
  40. private Texture? _markedFirstTexture;
  41. private readonly string _markedSecondTexturePath = "Storage/marked_second";
  42. private Texture? _markedSecondTexture;
  43. #endregion
  44. public ItemGridPiece(Entity<ItemComponent> entity, ItemStorageLocation location, IEntityManager entityManager)
  45. {
  46. IoCManager.InjectDependencies(this);
  47. _entityManager = entityManager;
  48. _storageController = UserInterfaceManager.GetUIController<StorageUIController>();
  49. Entity = entity.Owner;
  50. Location = location;
  51. Visible = true;
  52. MouseFilter = MouseFilterMode.Stop;
  53. TooltipSupplier = SupplyTooltip;
  54. OnThemeUpdated();
  55. }
  56. private Control? SupplyTooltip(Control sender)
  57. {
  58. if (_storageController.IsDragging)
  59. return null;
  60. return new Tooltip
  61. {
  62. Text = _entityManager.GetComponent<MetaDataComponent>(Entity).EntityName
  63. };
  64. }
  65. protected override void OnThemeUpdated()
  66. {
  67. base.OnThemeUpdated();
  68. _centerTexture = Theme.ResolveTextureOrNull(_centerTexturePath)?.Texture;
  69. _topTexture = Theme.ResolveTextureOrNull(_topTexturePath)?.Texture;
  70. _bottomTexture = Theme.ResolveTextureOrNull(_bottomTexturePath)?.Texture;
  71. _leftTexture = Theme.ResolveTextureOrNull(_leftTexturePath)?.Texture;
  72. _rightTexture = Theme.ResolveTextureOrNull(_rightTexturePath)?.Texture;
  73. _topLeftTexture = Theme.ResolveTextureOrNull(_topLeftTexturePath)?.Texture;
  74. _topRightTexture = Theme.ResolveTextureOrNull(_topRightTexturePath)?.Texture;
  75. _bottomLeftTexture = Theme.ResolveTextureOrNull(_bottomLeftTexturePath)?.Texture;
  76. _bottomRightTexture = Theme.ResolveTextureOrNull(_bottomRightTexturePath)?.Texture;
  77. _markedFirstTexture = Theme.ResolveTextureOrNull(_markedFirstTexturePath)?.Texture;
  78. _markedSecondTexture = Theme.ResolveTextureOrNull(_markedSecondTexturePath)?.Texture;
  79. }
  80. protected override void Draw(DrawingHandleScreen handle)
  81. {
  82. base.Draw(handle);
  83. // really just an "oh shit" catch.
  84. if (!_entityManager.EntityExists(Entity) || !_entityManager.TryGetComponent<ItemComponent>(Entity, out var itemComponent))
  85. {
  86. Dispose();
  87. return;
  88. }
  89. if (_storageController.IsDragging && _storageController.DraggingGhost?.Entity == Entity &&
  90. _storageController.DraggingGhost != this)
  91. {
  92. return;
  93. }
  94. var adjustedShape = _entityManager.System<ItemSystem>().GetAdjustedItemShape((Entity, itemComponent), Location.Rotation, Vector2i.Zero);
  95. var boundingGrid = adjustedShape.GetBoundingBox();
  96. var size = _centerTexture!.Size * 2 * UIScale;
  97. var hovering = !_storageController.IsDragging && UserInterfaceManager.CurrentlyHovered == this;
  98. //yeah, this coloring is kinda hardcoded. deal with it. B)
  99. Color? colorModulate = hovering ? null : Color.FromHex("#a8a8a8");
  100. var marked = Marked != null;
  101. Vector2i? maybeMarkedPos = null;
  102. _texturesPositions.Clear();
  103. for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
  104. {
  105. for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
  106. {
  107. if (!adjustedShape.Contains(x, y))
  108. continue;
  109. var offset = size * 2 * new Vector2(x - boundingGrid.Left, y - boundingGrid.Bottom);
  110. var topLeft = PixelPosition + offset.Floored();
  111. if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.NorthEast) is {} neTexture)
  112. {
  113. var neOffset = new Vector2(size.X, 0);
  114. handle.DrawTextureRect(neTexture, new UIBox2(topLeft + neOffset, topLeft + neOffset + size), colorModulate);
  115. }
  116. if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.NorthWest) is {} nwTexture)
  117. {
  118. _texturesPositions.Add((nwTexture, Position + offset / UIScale));
  119. handle.DrawTextureRect(nwTexture, new UIBox2(topLeft, topLeft + size), colorModulate);
  120. if (marked && nwTexture == _topLeftTexture)
  121. {
  122. maybeMarkedPos = topLeft;
  123. marked = false;
  124. }
  125. }
  126. if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.SouthEast) is {} seTexture)
  127. {
  128. var seOffset = size;
  129. handle.DrawTextureRect(seTexture, new UIBox2(topLeft + seOffset, topLeft + seOffset + size), colorModulate);
  130. }
  131. if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.SouthWest) is {} swTexture)
  132. {
  133. var swOffset = new Vector2(0, size.Y);
  134. handle.DrawTextureRect(swTexture, new UIBox2(topLeft + swOffset, topLeft + swOffset + size), colorModulate);
  135. }
  136. }
  137. }
  138. // typically you'd divide by two, but since the textures are half a tile, this is done implicitly
  139. var iconPosition = new Vector2((boundingGrid.Width + 1) * size.X + itemComponent.StoredOffset.X * 2,
  140. (boundingGrid.Height + 1) * size.Y + itemComponent.StoredOffset.Y * 2);
  141. var iconRotation = Location.Rotation + Angle.FromDegrees(itemComponent.StoredRotation);
  142. if (itemComponent.StoredSprite is { } storageSprite)
  143. {
  144. var scale = 2 * UIScale;
  145. var offset = (((Box2) boundingGrid).Size - Vector2.One) * size;
  146. var sprite = _entityManager.System<SpriteSystem>().Frame0(storageSprite);
  147. var spriteBox = new Box2Rotated(new Box2(0f, sprite.Height * scale, sprite.Width * scale, 0f), -iconRotation, Vector2.Zero);
  148. var root = spriteBox.CalcBoundingBox().BottomLeft;
  149. var pos = PixelPosition * 2
  150. + (Parent?.GlobalPixelPosition ?? Vector2.Zero)
  151. + offset;
  152. handle.SetTransform(pos, iconRotation);
  153. var box = new UIBox2(root, root + sprite.Size * scale);
  154. handle.DrawTextureRect(sprite, box);
  155. handle.SetTransform(GlobalPixelPosition, Angle.Zero);
  156. }
  157. else
  158. {
  159. _entityManager.System<SpriteSystem>().ForceUpdate(Entity);
  160. handle.DrawEntity(Entity,
  161. PixelPosition + iconPosition,
  162. Vector2.One * 2 * UIScale,
  163. Angle.Zero,
  164. eyeRotation: iconRotation,
  165. overrideDirection: Direction.South);
  166. }
  167. if (maybeMarkedPos is {} markedPos)
  168. {
  169. var markedTexture = Marked switch
  170. {
  171. ItemGridPieceMarks.First => _markedFirstTexture,
  172. ItemGridPieceMarks.Second => _markedSecondTexture,
  173. _ => null,
  174. };
  175. if (markedTexture != null)
  176. {
  177. handle.DrawTextureRect(markedTexture, new UIBox2(markedPos, markedPos + size));
  178. }
  179. }
  180. }
  181. protected override bool HasPoint(Vector2 point)
  182. {
  183. foreach (var (texture, position) in _texturesPositions)
  184. {
  185. if (!new Box2(position, position + texture.Size * 4).Contains(point))
  186. continue;
  187. return true;
  188. }
  189. return false;
  190. }
  191. protected override void KeyBindDown(GUIBoundKeyEventArgs args)
  192. {
  193. base.KeyBindDown(args);
  194. OnPiecePressed?.Invoke(args, this);
  195. }
  196. protected override void KeyBindUp(GUIBoundKeyEventArgs args)
  197. {
  198. base.KeyBindUp(args);
  199. OnPieceUnpressed?.Invoke(args, this);
  200. }
  201. private Texture? GetTexture(IReadOnlyList<Box2i> boxes, Vector2i position, Direction corner)
  202. {
  203. var top = !boxes.Contains(position - Vector2i.Up);
  204. var bottom = !boxes.Contains(position - Vector2i.Down);
  205. var left = !boxes.Contains(position + Vector2i.Left);
  206. var right = !boxes.Contains(position + Vector2i.Right);
  207. switch (corner)
  208. {
  209. case Direction.NorthEast:
  210. if (top && right)
  211. return _topRightTexture;
  212. if (top)
  213. return _topTexture;
  214. if (right)
  215. return _rightTexture;
  216. return _centerTexture;
  217. case Direction.NorthWest:
  218. if (top && left)
  219. return _topLeftTexture;
  220. if (top)
  221. return _topTexture;
  222. if (left)
  223. return _leftTexture;
  224. return _centerTexture;
  225. case Direction.SouthEast:
  226. if (bottom && right)
  227. return _bottomRightTexture;
  228. if (bottom)
  229. return _bottomTexture;
  230. if (right)
  231. return _rightTexture;
  232. return _centerTexture;
  233. case Direction.SouthWest:
  234. if (bottom && left)
  235. return _bottomLeftTexture;
  236. if (bottom)
  237. return _bottomTexture;
  238. if (left)
  239. return _leftTexture;
  240. return _centerTexture;
  241. default:
  242. return null;
  243. }
  244. }
  245. public static Vector2 GetCenterOffset(Entity<ItemComponent?> entity, ItemStorageLocation location, IEntityManager entMan)
  246. {
  247. var boxSize = entMan.System<ItemSystem>().GetAdjustedItemShape(entity, location).GetBoundingBox().Size;
  248. var actualSize = new Vector2(boxSize.X + 1, boxSize.Y + 1);
  249. return actualSize * new Vector2i(8, 8);
  250. }
  251. public EntityUid? UiEntity => Entity;
  252. }
  253. public enum ItemGridPieceMarks
  254. {
  255. First,
  256. Second,
  257. }