InventoryUIController.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. using System.Linq;
  2. using System.Numerics;
  3. using Content.Client.Gameplay;
  4. using Content.Client.Hands.Systems;
  5. using Content.Client.Inventory;
  6. using Content.Client.Storage.Systems;
  7. using Content.Client.UserInterface.Controls;
  8. using Content.Client.UserInterface.Systems.Gameplay;
  9. using Content.Client.UserInterface.Systems.Inventory.Controls;
  10. using Content.Client.UserInterface.Systems.Inventory.Widgets;
  11. using Content.Client.UserInterface.Systems.Inventory.Windows;
  12. using Content.Shared.Containers.ItemSlots;
  13. using Content.Shared.Hands.Components;
  14. using Content.Shared.Input;
  15. using Content.Shared.Inventory.VirtualItem;
  16. using Content.Shared.Storage;
  17. using Robust.Client.GameObjects;
  18. using Robust.Client.UserInterface;
  19. using Robust.Client.UserInterface.Controllers;
  20. using Robust.Client.UserInterface.Controls;
  21. using Robust.Shared.Input;
  22. using Robust.Shared.Input.Binding;
  23. using Robust.Shared.Map;
  24. using Robust.Shared.Player;
  25. using Robust.Shared.Utility;
  26. using static Content.Client.Inventory.ClientInventorySystem;
  27. namespace Content.Client.UserInterface.Systems.Inventory;
  28. public sealed class InventoryUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>,
  29. IOnSystemChanged<ClientInventorySystem>, IOnSystemChanged<HandsSystem>
  30. {
  31. [Dependency] private readonly IEntityManager _entities = default!;
  32. [UISystemDependency] private readonly ClientInventorySystem _inventorySystem = default!;
  33. [UISystemDependency] private readonly HandsSystem _handsSystem = default!;
  34. [UISystemDependency] private readonly ContainerSystem _container = default!;
  35. private EntityUid? _playerUid;
  36. private InventorySlotsComponent? _playerInventory;
  37. private readonly Dictionary<string, ItemSlotButtonContainer> _slotGroups = new();
  38. private StrippingWindow? _strippingWindow;
  39. private ItemSlotButtonContainer? _inventoryHotbar;
  40. private SlotButton? _inventoryButton;
  41. private SlotControl? _lastHovered;
  42. public override void Initialize()
  43. {
  44. base.Initialize();
  45. var gameplayStateLoad = UIManager.GetUIController<GameplayStateLoadController>();
  46. gameplayStateLoad.OnScreenLoad += OnScreenLoad;
  47. }
  48. private void OnScreenLoad()
  49. {
  50. if (UIManager.ActiveScreen == null)
  51. return;
  52. if (UIManager.GetActiveUIWidgetOrNull<InventoryGui>() is { } inventoryGui)
  53. RegisterInventoryButton(inventoryGui.InventoryButton);
  54. }
  55. public void OnStateEntered(GameplayState state)
  56. {
  57. DebugTools.Assert(_strippingWindow == null);
  58. _strippingWindow = UIManager.CreateWindow<StrippingWindow>();
  59. LayoutContainer.SetAnchorPreset(_strippingWindow, LayoutContainer.LayoutPreset.Center);
  60. //bind open inventory key to OpenInventoryMenu;
  61. CommandBinds.Builder
  62. .Bind(ContentKeyFunctions.OpenInventoryMenu, InputCmdHandler.FromDelegate(_ => ToggleInventoryBar()))
  63. .Register<ClientInventorySystem>();
  64. }
  65. public void OnStateExited(GameplayState state)
  66. {
  67. if (_strippingWindow != null)
  68. {
  69. _strippingWindow.Dispose();
  70. _strippingWindow = null;
  71. }
  72. if (_inventoryHotbar != null)
  73. {
  74. _inventoryHotbar.Visible = false;
  75. }
  76. CommandBinds.Unregister<ClientInventorySystem>();
  77. }
  78. private SlotButton CreateSlotButton(SlotData data)
  79. {
  80. var button = new SlotButton(data);
  81. button.Pressed += ItemPressed;
  82. button.StoragePressed += StoragePressed;
  83. button.Hover += SlotButtonHovered;
  84. return button;
  85. }
  86. public void RegisterInventoryBarContainer(ItemSlotButtonContainer inventoryHotbar)
  87. {
  88. _inventoryHotbar = inventoryHotbar;
  89. }
  90. public void RegisterInventoryButton(SlotButton? button)
  91. {
  92. if (_inventoryButton != null)
  93. {
  94. _inventoryButton.Pressed -= InventoryButtonPressed;
  95. }
  96. if (button != null)
  97. {
  98. _inventoryButton = button;
  99. _inventoryButton.Pressed += InventoryButtonPressed;
  100. }
  101. }
  102. private void InventoryButtonPressed(GUIBoundKeyEventArgs args, SlotControl control)
  103. {
  104. if (args.Function != EngineKeyFunctions.UIClick)
  105. return;
  106. ToggleInventoryBar();
  107. }
  108. private void UpdateInventoryHotbar(InventorySlotsComponent? clientInv)
  109. {
  110. if (clientInv == null)
  111. {
  112. _inventoryHotbar?.ClearButtons();
  113. if (_inventoryButton != null)
  114. _inventoryButton.Visible = false;
  115. return;
  116. }
  117. foreach (var (_, data) in clientInv.SlotData)
  118. {
  119. if (!data.ShowInWindow || !_slotGroups.TryGetValue(data.SlotGroup, out var container))
  120. continue;
  121. if (!container.TryGetButton(data.SlotName, out var button))
  122. {
  123. button = CreateSlotButton(data);
  124. container.AddButton(button);
  125. }
  126. var showStorage = _entities.HasComponent<StorageComponent>(data.HeldEntity);
  127. var update = new SlotSpriteUpdate(data.HeldEntity, data.SlotGroup, data.SlotName, showStorage);
  128. SpriteUpdated(update);
  129. }
  130. if (_inventoryHotbar == null)
  131. return;
  132. var clothing = clientInv.SlotData.Where(p => !p.Value.HasSlotGroup).ToList();
  133. if (_inventoryButton != null)
  134. _inventoryButton.Visible = clothing.Count != 0;
  135. if (clothing.Count == 0)
  136. return;
  137. foreach (var child in new List<Control>(_inventoryHotbar.Children))
  138. {
  139. if (child is not SlotControl)
  140. _inventoryHotbar.RemoveChild(child);
  141. }
  142. var maxWidth = clothing.Max(p => p.Value.ButtonOffset.X) + 1;
  143. var maxIndex = clothing.Select(p => GetIndex(p.Value.ButtonOffset)).Max();
  144. _inventoryHotbar.MaxColumns = maxWidth;
  145. _inventoryHotbar.Columns = maxWidth;
  146. for (var i = 0; i <= maxIndex; i++)
  147. {
  148. var index = i;
  149. if (clothing.FirstOrNull(p => GetIndex(p.Value.ButtonOffset) == index) is { } pair)
  150. {
  151. if (_inventoryHotbar.TryGetButton(pair.Key, out var slot))
  152. slot.SetPositionLast();
  153. }
  154. else
  155. {
  156. _inventoryHotbar.AddChild(new Control
  157. {
  158. MinSize = new Vector2(64, 64)
  159. });
  160. }
  161. }
  162. return;
  163. int GetIndex(Vector2i position)
  164. {
  165. return position.Y * maxWidth + position.X;
  166. }
  167. }
  168. private void UpdateStrippingWindow(InventorySlotsComponent? clientInv)
  169. {
  170. if (clientInv == null)
  171. {
  172. _strippingWindow!.InventoryButtons.ClearButtons();
  173. return;
  174. }
  175. foreach (var (_, data) in clientInv.SlotData)
  176. {
  177. if (!data.ShowInWindow)
  178. continue;
  179. if (!_strippingWindow!.InventoryButtons.TryGetButton(data.SlotName, out var button))
  180. {
  181. button = CreateSlotButton(data);
  182. _strippingWindow!.InventoryButtons.AddButton(button, data.ButtonOffset);
  183. }
  184. var showStorage = _entities.HasComponent<StorageComponent>(data.HeldEntity);
  185. var update = new SlotSpriteUpdate(data.HeldEntity, data.SlotGroup, data.SlotName, showStorage);
  186. SpriteUpdated(update);
  187. }
  188. }
  189. public void ToggleStrippingMenu()
  190. {
  191. UpdateStrippingWindow(_playerInventory);
  192. if (_strippingWindow!.IsOpen)
  193. {
  194. _strippingWindow!.Close();
  195. return;
  196. }
  197. _strippingWindow.Open();
  198. }
  199. public void ToggleInventoryBar()
  200. {
  201. if (_inventoryHotbar == null)
  202. {
  203. Logger.Warning("Tried to toggle inventory bar when none are assigned");
  204. return;
  205. }
  206. UpdateInventoryHotbar(_playerInventory);
  207. var shouldBeVisible = !_inventoryHotbar.Visible;
  208. _inventoryHotbar.Visible = shouldBeVisible;
  209. }
  210. // Neuron Activation
  211. public void OnSystemLoaded(ClientInventorySystem system)
  212. {
  213. _inventorySystem.OnSlotAdded += AddSlot;
  214. _inventorySystem.OnSlotRemoved += RemoveSlot;
  215. _inventorySystem.OnLinkInventorySlots += LoadSlots;
  216. _inventorySystem.OnUnlinkInventory += UnloadSlots;
  217. _inventorySystem.OnSpriteUpdate += SpriteUpdated;
  218. }
  219. // Neuron Deactivation
  220. public void OnSystemUnloaded(ClientInventorySystem system)
  221. {
  222. _inventorySystem.OnSlotAdded -= AddSlot;
  223. _inventorySystem.OnSlotRemoved -= RemoveSlot;
  224. _inventorySystem.OnLinkInventorySlots -= LoadSlots;
  225. _inventorySystem.OnUnlinkInventory -= UnloadSlots;
  226. _inventorySystem.OnSpriteUpdate -= SpriteUpdated;
  227. }
  228. private void ItemPressed(GUIBoundKeyEventArgs args, SlotControl control)
  229. {
  230. var slot = control.SlotName;
  231. if (args.Function == EngineKeyFunctions.UIClick)
  232. {
  233. _inventorySystem.UIInventoryActivate(control.SlotName);
  234. args.Handle();
  235. return;
  236. }
  237. if (_playerInventory == null || _playerUid == null)
  238. {
  239. return;
  240. }
  241. if (args.Function == ContentKeyFunctions.ExamineEntity)
  242. {
  243. _inventorySystem.UIInventoryExamine(slot, _playerUid.Value);
  244. }
  245. else if (args.Function == EngineKeyFunctions.UseSecondary)
  246. {
  247. _inventorySystem.UIInventoryOpenContextMenu(slot, _playerUid.Value);
  248. }
  249. else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
  250. {
  251. _inventorySystem.UIInventoryActivateItem(slot, _playerUid.Value);
  252. }
  253. else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
  254. {
  255. _inventorySystem.UIInventoryAltActivateItem(slot, _playerUid.Value);
  256. }
  257. else
  258. {
  259. return;
  260. }
  261. args.Handle();
  262. }
  263. private void StoragePressed(GUIBoundKeyEventArgs args, SlotControl control)
  264. {
  265. _inventorySystem.UIInventoryStorageActivate(control.SlotName);
  266. }
  267. private void SlotButtonHovered(GUIMouseHoverEventArgs args, SlotControl control)
  268. {
  269. UpdateHover(control);
  270. _lastHovered = control;
  271. }
  272. public void UpdateHover(SlotControl control)
  273. {
  274. var player = _playerUid;
  275. if (!control.MouseIsHovering ||
  276. _playerInventory == null ||
  277. !_entities.TryGetComponent<HandsComponent>(player, out var hands) ||
  278. hands.ActiveHandEntity is not { } held ||
  279. !_entities.TryGetComponent(held, out SpriteComponent? sprite) ||
  280. !_inventorySystem.TryGetSlotContainer(player.Value, control.SlotName, out var container, out var slotDef))
  281. {
  282. control.ClearHover();
  283. return;
  284. }
  285. // Set green / red overlay at 50% transparency
  286. var hoverEntity = _entities.SpawnEntity("hoverentity", MapCoordinates.Nullspace);
  287. var hoverSprite = _entities.GetComponent<SpriteComponent>(hoverEntity);
  288. var fits = _inventorySystem.CanEquip(player.Value, held, control.SlotName, out _, slotDef) &&
  289. _container.CanInsert(held, container);
  290. if (!fits && _entities.TryGetComponent<StorageComponent>(container.ContainedEntity, out var storage))
  291. {
  292. fits = _entities.System<StorageSystem>().CanInsert(container.ContainedEntity.Value, held, out _, storage);
  293. }
  294. else if (!fits && _entities.TryGetComponent<ItemSlotsComponent>(container.ContainedEntity, out var itemSlots))
  295. {
  296. var itemSlotsSys = _entities.System<ItemSlotsSystem>();
  297. foreach (var slot in itemSlots.Slots.Values)
  298. {
  299. if (!slot.InsertOnInteract)
  300. continue;
  301. if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held, null, slot))
  302. continue;
  303. fits = true;
  304. break;
  305. }
  306. }
  307. hoverSprite.CopyFrom(sprite);
  308. hoverSprite.Color = fits ? new Color(0, 255, 0, 127) : new Color(255, 0, 0, 127);
  309. control.HoverSpriteView.SetEntity(hoverEntity);
  310. }
  311. private void AddSlot(SlotData data)
  312. {
  313. if (!_slotGroups.TryGetValue(data.SlotGroup, out var slotGroup))
  314. return;
  315. var button = CreateSlotButton(data);
  316. slotGroup.AddButton(button);
  317. }
  318. private void RemoveSlot(SlotData data)
  319. {
  320. if (!_slotGroups.TryGetValue(data.SlotGroup, out var slotGroup))
  321. return;
  322. slotGroup.RemoveButton(data.SlotName);
  323. }
  324. public void ReloadSlots()
  325. {
  326. _inventorySystem.ReloadInventory();
  327. }
  328. private void LoadSlots(EntityUid clientUid, InventorySlotsComponent clientInv)
  329. {
  330. UnloadSlots();
  331. _playerUid = clientUid;
  332. _playerInventory = clientInv;
  333. foreach (var slotData in clientInv.SlotData.Values)
  334. {
  335. AddSlot(slotData);
  336. if (_inventoryButton != null)
  337. _inventoryButton.Visible = true;
  338. }
  339. UpdateInventoryHotbar(_playerInventory);
  340. }
  341. private void UnloadSlots()
  342. {
  343. if (_inventoryButton != null)
  344. _inventoryButton.Visible = false;
  345. _playerUid = null;
  346. _playerInventory = null;
  347. foreach (var slotGroup in _slotGroups.Values)
  348. {
  349. slotGroup.ClearButtons();
  350. }
  351. UpdateInventoryHotbar(null);
  352. }
  353. private void SpriteUpdated(SlotSpriteUpdate update)
  354. {
  355. var (entity, group, name, showStorage) = update;
  356. if (_strippingWindow?.InventoryButtons.GetButton(update.Name) is { } inventoryButton)
  357. {
  358. inventoryButton.SetEntity(entity);
  359. inventoryButton.StorageButton.Visible = showStorage;
  360. }
  361. if (_slotGroups.GetValueOrDefault(group)?.GetButton(name) is not { } button)
  362. return;
  363. if (_entities.TryGetComponent(entity, out VirtualItemComponent? virtb))
  364. {
  365. button.SetEntity(virtb.BlockingEntity);
  366. button.Blocked = true;
  367. }
  368. else
  369. {
  370. button.SetEntity(entity);
  371. button.Blocked = false;
  372. button.StorageButton.Visible = showStorage;
  373. }
  374. }
  375. public bool RegisterSlotGroupContainer(ItemSlotButtonContainer slotContainer)
  376. {
  377. if (_slotGroups.TryAdd(slotContainer.SlotGroup, slotContainer))
  378. return true;
  379. return false;
  380. }
  381. public void RemoveSlotGroup(string slotGroupName)
  382. {
  383. _slotGroups.Remove(slotGroupName);
  384. }
  385. // Monkey Sees Action
  386. // Neuron Activation
  387. // Monkey copies code
  388. public void OnSystemLoaded(HandsSystem system)
  389. {
  390. _handsSystem.OnPlayerItemAdded += OnItemAdded;
  391. _handsSystem.OnPlayerItemRemoved += OnItemRemoved;
  392. _handsSystem.OnPlayerSetActiveHand += SetActiveHand;
  393. }
  394. public void OnSystemUnloaded(HandsSystem system)
  395. {
  396. _handsSystem.OnPlayerItemAdded -= OnItemAdded;
  397. _handsSystem.OnPlayerItemRemoved -= OnItemRemoved;
  398. _handsSystem.OnPlayerSetActiveHand -= SetActiveHand;
  399. }
  400. private void OnItemAdded(string name, EntityUid entity)
  401. {
  402. if (_lastHovered != null)
  403. UpdateHover(_lastHovered);
  404. }
  405. private void OnItemRemoved(string name, EntityUid entity)
  406. {
  407. if (_lastHovered != null)
  408. UpdateHover(_lastHovered);
  409. }
  410. private void SetActiveHand(string? handName)
  411. {
  412. if (_lastHovered != null)
  413. UpdateHover(_lastHovered);
  414. }
  415. }