StrippableBoundUserInterface.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using System.Linq;
  2. using System.Numerics;
  3. using Content.Client.Examine;
  4. using Content.Client.Strip;
  5. using Content.Client.Stylesheets;
  6. using Content.Client.UserInterface.Controls;
  7. using Content.Client.UserInterface.Systems.Hands.Controls;
  8. using Content.Client.Verbs.UI;
  9. using Content.Shared.Cuffs;
  10. using Content.Shared.Cuffs.Components;
  11. using Content.Shared.Ensnaring.Components;
  12. using Content.Shared.Hands.Components;
  13. using Content.Shared.IdentityManagement;
  14. using Content.Shared.Input;
  15. using Content.Shared.Inventory;
  16. using Content.Shared.Inventory.VirtualItem;
  17. using Content.Shared.Strip.Components;
  18. using JetBrains.Annotations;
  19. using Robust.Client.GameObjects;
  20. using Robust.Client.Player;
  21. using Robust.Client.UserInterface;
  22. using Robust.Client.UserInterface.Controls;
  23. using Robust.Shared.Input;
  24. using Robust.Shared.Map;
  25. using static Content.Client.Inventory.ClientInventorySystem;
  26. using static Robust.Client.UserInterface.Control;
  27. namespace Content.Client.Inventory
  28. {
  29. [UsedImplicitly]
  30. public sealed class StrippableBoundUserInterface : BoundUserInterface
  31. {
  32. [Dependency] private readonly IPlayerManager _player = default!;
  33. [Dependency] private readonly IUserInterfaceManager _ui = default!;
  34. private readonly ExamineSystem _examine;
  35. private readonly InventorySystem _inv;
  36. private readonly SharedCuffableSystem _cuffable;
  37. private readonly StrippableSystem _strippable;
  38. [ViewVariables]
  39. private const int ButtonSeparation = 4;
  40. [ViewVariables]
  41. public const string HiddenPocketEntityId = "StrippingHiddenEntity";
  42. [ViewVariables]
  43. private StrippingMenu? _strippingMenu;
  44. [ViewVariables]
  45. private readonly EntityUid _virtualHiddenEntity;
  46. public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
  47. {
  48. _examine = EntMan.System<ExamineSystem>();
  49. _inv = EntMan.System<InventorySystem>();
  50. _cuffable = EntMan.System<SharedCuffableSystem>();
  51. _strippable = EntMan.System<StrippableSystem>();
  52. _virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace);
  53. }
  54. protected override void Open()
  55. {
  56. base.Open();
  57. _strippingMenu = this.CreateWindowCenteredLeft<StrippingMenu>();
  58. _strippingMenu.OnDirty += UpdateMenu;
  59. _strippingMenu.Title = Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner, EntMan)));
  60. }
  61. protected override void Dispose(bool disposing)
  62. {
  63. if (!disposing)
  64. return;
  65. if (_strippingMenu != null)
  66. _strippingMenu.OnDirty -= UpdateMenu;
  67. EntMan.DeleteEntity(_virtualHiddenEntity);
  68. base.Dispose(disposing);
  69. }
  70. public void DirtyMenu()
  71. {
  72. if (_strippingMenu != null)
  73. _strippingMenu.Dirty = true;
  74. }
  75. public void UpdateMenu()
  76. {
  77. if (_strippingMenu == null)
  78. return;
  79. _strippingMenu.ClearButtons();
  80. if (EntMan.TryGetComponent<InventoryComponent>(Owner, out var inv))
  81. {
  82. foreach (var slot in inv.Slots)
  83. {
  84. AddInventoryButton(Owner, slot.Name, inv);
  85. }
  86. }
  87. if (EntMan.TryGetComponent<HandsComponent>(Owner, out var handsComp) && handsComp.CanBeStripped)
  88. {
  89. // good ol hands shit code. there is a GuiHands comparer that does the same thing... but these are hands
  90. // and not gui hands... which are different...
  91. foreach (var hand in handsComp.Hands.Values)
  92. {
  93. if (hand.Location != HandLocation.Right)
  94. continue;
  95. AddHandButton(hand);
  96. }
  97. foreach (var hand in handsComp.Hands.Values)
  98. {
  99. if (hand.Location != HandLocation.Middle)
  100. continue;
  101. AddHandButton(hand);
  102. }
  103. foreach (var hand in handsComp.Hands.Values)
  104. {
  105. if (hand.Location != HandLocation.Left)
  106. continue;
  107. AddHandButton(hand);
  108. }
  109. }
  110. // snare-removal button. This is just the old button before the change to item slots. It is pretty out of place.
  111. if (EntMan.TryGetComponent<EnsnareableComponent>(Owner, out var snare) && snare.IsEnsnared)
  112. {
  113. var button = new Button()
  114. {
  115. Text = Loc.GetString("strippable-bound-user-interface-stripping-menu-ensnare-button"),
  116. StyleClasses = { StyleBase.ButtonOpenRight }
  117. };
  118. button.OnPressed += (_) => SendPredictedMessage(new StrippingEnsnareButtonPressed());
  119. _strippingMenu.SnareContainer.AddChild(button);
  120. }
  121. // TODO fix layout container measuring (its broken atm).
  122. // _strippingMenu.InvalidateMeasure();
  123. // _strippingMenu.Contents.Measure(Vector2Helpers.Infinity);
  124. // TODO allow windows to resize based on content's desired size
  125. // for now: shit-code
  126. // this breaks for drones (too many hands, lots of empty vertical space), and looks shit for monkeys and the like.
  127. // but the window is realizable, so eh.
  128. _strippingMenu.SetSize = new Vector2(220, snare?.IsEnsnared == true ? 550 : 530);
  129. }
  130. private void AddHandButton(Hand hand)
  131. {
  132. var button = new HandButton(hand.Name, hand.Location);
  133. button.Pressed += SlotPressed;
  134. if (EntMan.TryGetComponent<VirtualItemComponent>(hand.HeldEntity, out var virt))
  135. {
  136. button.Blocked = true;
  137. if (EntMan.TryGetComponent<CuffableComponent>(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity))
  138. button.BlockedRect.MouseFilter = MouseFilterMode.Ignore;
  139. }
  140. UpdateEntityIcon(button, hand.HeldEntity);
  141. _strippingMenu!.HandsContainer.AddChild(button);
  142. }
  143. private void SlotPressed(GUIBoundKeyEventArgs ev, SlotControl slot)
  144. {
  145. // TODO: allow other interactions? Verbs? But they should then generate a pop-up and/or have a delay so the
  146. // user that is being stripped can prevent the verbs from being exectuted.
  147. // So for now: only stripping & examining
  148. if (ev.Function == EngineKeyFunctions.Use)
  149. {
  150. SendPredictedMessage(new StrippingSlotButtonPressed(slot.SlotName, slot is HandButton));
  151. return;
  152. }
  153. if (slot.Entity == null)
  154. return;
  155. if (ev.Function == ContentKeyFunctions.ExamineEntity)
  156. {
  157. _examine.DoExamine(slot.Entity.Value);
  158. ev.Handle();
  159. }
  160. else if (ev.Function == EngineKeyFunctions.UseSecondary)
  161. {
  162. _ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(slot.Entity.Value);
  163. ev.Handle();
  164. }
  165. }
  166. private void AddInventoryButton(EntityUid invUid, string slotId, InventoryComponent inv)
  167. {
  168. if (!_inv.TryGetSlotContainer(invUid, slotId, out var container, out var slotDef, inv))
  169. return;
  170. var entity = container.ContainedEntity;
  171. // If this is a full pocket, obscure the real entity
  172. // this does not work for modified clients because they are still sent the real entity
  173. if (entity != null && _strippable.IsStripHidden(slotDef, _player.LocalEntity))
  174. entity = _virtualHiddenEntity;
  175. var button = new SlotButton(new SlotData(slotDef, container));
  176. button.Pressed += SlotPressed;
  177. _strippingMenu!.InventoryContainer.AddChild(button);
  178. UpdateEntityIcon(button, entity);
  179. LayoutContainer.SetPosition(button, slotDef.StrippingWindowPos * (SlotControl.DefaultButtonSize + ButtonSeparation));
  180. }
  181. private void UpdateEntityIcon(SlotControl button, EntityUid? entity)
  182. {
  183. // Hovering, highlighting & storage are features of general hands & inv GUIs. This UI just re-uses these because I'm lazy.
  184. button.ClearHover();
  185. button.StorageButton.Visible = false;
  186. if (entity == null)
  187. {
  188. button.SetEntity(null);
  189. return;
  190. }
  191. EntityUid? viewEnt;
  192. if (EntMan.TryGetComponent<VirtualItemComponent>(entity, out var virt))
  193. viewEnt = EntMan.HasComponent<SpriteComponent>(virt.BlockingEntity) ? virt.BlockingEntity : null;
  194. else if (EntMan.HasComponent<SpriteComponent>(entity))
  195. viewEnt = entity;
  196. else
  197. return;
  198. button.SetEntity(viewEnt);
  199. }
  200. }
  201. }