VendingMachineMenu.xaml.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. using System.Linq;
  2. using System.Numerics;
  3. using Content.Shared.VendingMachines;
  4. using Robust.Client.AutoGenerated;
  5. using Robust.Client.UserInterface.Controls;
  6. using Robust.Client.UserInterface.XAML;
  7. using Robust.Shared.Prototypes;
  8. using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
  9. using Robust.Client.UserInterface;
  10. using Content.Client.UserInterface.Controls;
  11. using Content.Shared.IdentityManagement;
  12. using Robust.Client.Graphics;
  13. namespace Content.Client.VendingMachines.UI
  14. {
  15. [GenerateTypedNameReferences]
  16. public sealed partial class VendingMachineMenu : FancyWindow
  17. {
  18. [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
  19. [Dependency] private readonly IEntityManager _entityManager = default!;
  20. private readonly Dictionary<EntProtoId, EntityUid> _dummies = [];
  21. private readonly Dictionary<EntProtoId, (ListContainerButton Button, VendingMachineItem Item)> _listItems = new();
  22. private readonly Dictionary<EntProtoId, uint> _amounts = new();
  23. /// <summary>
  24. /// Whether the vending machine is able to be interacted with or not.
  25. /// </summary>
  26. private bool _enabled;
  27. public event Action<GUIBoundKeyEventArgs, ListData>? OnItemSelected;
  28. public VendingMachineMenu()
  29. {
  30. MinSize = SetSize = new Vector2(250, 150);
  31. RobustXamlLoader.Load(this);
  32. IoCManager.InjectDependencies(this);
  33. VendingContents.SearchBar = SearchBar;
  34. VendingContents.DataFilterCondition += DataFilterCondition;
  35. VendingContents.GenerateItem += GenerateButton;
  36. VendingContents.ItemKeyBindDown += (args, data) => OnItemSelected?.Invoke(args, data);
  37. }
  38. protected override void Dispose(bool disposing)
  39. {
  40. base.Dispose(disposing);
  41. // Don't clean up dummies during disposal or we'll just have to spawn them again
  42. if (!disposing)
  43. return;
  44. // Delete any dummy items we spawned
  45. foreach (var entity in _dummies.Values)
  46. {
  47. _entityManager.QueueDeleteEntity(entity);
  48. }
  49. _dummies.Clear();
  50. }
  51. private bool DataFilterCondition(string filter, ListData data)
  52. {
  53. if (data is not VendorItemsListData { ItemText: var text })
  54. return false;
  55. if (string.IsNullOrEmpty(filter))
  56. return true;
  57. return text.Contains(filter, StringComparison.CurrentCultureIgnoreCase);
  58. }
  59. private void GenerateButton(ListData data, ListContainerButton button)
  60. {
  61. if (data is not VendorItemsListData { ItemProtoID: var protoID, ItemText: var text })
  62. return;
  63. var item = new VendingMachineItem(protoID, text);
  64. _listItems[protoID] = (button, item);
  65. button.AddChild(item);
  66. button.AddStyleClass("ButtonSquare");
  67. button.Disabled = !_enabled || _amounts[protoID] == 0;
  68. }
  69. /// <summary>
  70. /// Populates the list of available items on the vending machine interface
  71. /// and sets icons based on their prototypes
  72. /// </summary>
  73. public void Populate(List<VendingMachineInventoryEntry> inventory, bool enabled)
  74. {
  75. _enabled = enabled;
  76. _listItems.Clear();
  77. _amounts.Clear();
  78. if (inventory.Count == 0 && VendingContents.Visible)
  79. {
  80. SearchBar.Visible = false;
  81. VendingContents.Visible = false;
  82. var outOfStockLabel = new Label()
  83. {
  84. Text = Loc.GetString("vending-machine-component-try-eject-out-of-stock"),
  85. Margin = new Thickness(4, 4),
  86. HorizontalExpand = true,
  87. VerticalAlignment = VAlignment.Stretch,
  88. HorizontalAlignment = HAlignment.Center
  89. };
  90. MainContainer.AddChild(outOfStockLabel);
  91. SetSizeAfterUpdate(outOfStockLabel.Text.Length, 0);
  92. return;
  93. }
  94. var longestEntry = string.Empty;
  95. var listData = new List<VendorItemsListData>();
  96. for (var i = 0; i < inventory.Count; i++)
  97. {
  98. var entry = inventory[i];
  99. if (!_prototypeManager.TryIndex(entry.ID, out var prototype))
  100. {
  101. _amounts[entry.ID] = 0;
  102. continue;
  103. }
  104. if (!_dummies.TryGetValue(entry.ID, out var dummy))
  105. {
  106. dummy = _entityManager.Spawn(entry.ID);
  107. _dummies.Add(entry.ID, dummy);
  108. }
  109. var itemName = Identity.Name(dummy, _entityManager);
  110. var itemText = $"{itemName} [{entry.Amount}]";
  111. _amounts[entry.ID] = entry.Amount;
  112. if (itemText.Length > longestEntry.Length)
  113. longestEntry = itemText;
  114. listData.Add(new VendorItemsListData(prototype.ID, i)
  115. {
  116. ItemText = itemText,
  117. });
  118. }
  119. VendingContents.PopulateList(listData);
  120. SetSizeAfterUpdate(longestEntry.Length, inventory.Count);
  121. }
  122. /// <summary>
  123. /// Updates text entries for vending data in place without modifying the list controls.
  124. /// </summary>
  125. public void UpdateAmounts(List<VendingMachineInventoryEntry> cachedInventory, bool enabled)
  126. {
  127. _enabled = enabled;
  128. foreach (var proto in _dummies.Keys)
  129. {
  130. if (!_listItems.TryGetValue(proto, out var button))
  131. continue;
  132. var dummy = _dummies[proto];
  133. var amount = cachedInventory.First(o => o.ID == proto).Amount;
  134. // Could be better? Problem is all inventory entries get squashed.
  135. var text = GetItemText(dummy, amount);
  136. button.Item.SetText(text);
  137. button.Button.Disabled = !enabled || amount == 0;
  138. }
  139. }
  140. private string GetItemText(EntityUid dummy, uint amount)
  141. {
  142. var itemName = Identity.Name(dummy, _entityManager);
  143. return $"{itemName} [{amount}]";
  144. }
  145. private void SetSizeAfterUpdate(int longestEntryLength, int contentCount)
  146. {
  147. SetSize = new Vector2(Math.Clamp((longestEntryLength + 2) * 12, 250, 400),
  148. Math.Clamp(contentCount * 50, 150, 350));
  149. }
  150. }
  151. public record VendorItemsListData(EntProtoId ItemProtoID, int ItemIndex) : ListData
  152. {
  153. public string ItemText = string.Empty;
  154. }
  155. }