SharedHandsSystem.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Linq;
  3. using Content.Shared.ActionBlocker;
  4. using Content.Shared.Administration.Logs;
  5. using Content.Shared.Hands.Components;
  6. using Content.Shared.Interaction;
  7. using Content.Shared.Inventory;
  8. using Content.Shared.Inventory.VirtualItem;
  9. using Content.Shared.Storage.EntitySystems;
  10. using Robust.Shared.Containers;
  11. using Robust.Shared.Input.Binding;
  12. namespace Content.Shared.Hands.EntitySystems;
  13. public abstract partial class SharedHandsSystem
  14. {
  15. [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
  16. [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
  17. [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
  18. [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
  19. [Dependency] private readonly InventorySystem _inventory = default!;
  20. [Dependency] private readonly SharedStorageSystem _storage = default!;
  21. [Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
  22. [Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!;
  23. protected event Action<Entity<HandsComponent>?>? OnHandSetActive;
  24. public override void Initialize()
  25. {
  26. base.Initialize();
  27. InitializeInteractions();
  28. InitializeDrop();
  29. InitializePickup();
  30. InitializeRelay();
  31. }
  32. public override void Shutdown()
  33. {
  34. base.Shutdown();
  35. CommandBinds.Unregister<SharedHandsSystem>();
  36. }
  37. public virtual void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null)
  38. {
  39. if (!Resolve(uid, ref handsComp, false))
  40. return;
  41. if (handsComp.Hands.ContainsKey(handName))
  42. return;
  43. var container = ContainerSystem.EnsureContainer<ContainerSlot>(uid, handName);
  44. container.OccludesLight = false;
  45. var newHand = new Hand(handName, handLocation, container);
  46. handsComp.Hands.Add(handName, newHand);
  47. AddToSortedHands(handsComp, handName, handLocation); // Shitmed Change
  48. if (handsComp.ActiveHand == null)
  49. SetActiveHand(uid, newHand, handsComp);
  50. RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
  51. Dirty(uid, handsComp);
  52. }
  53. public virtual void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
  54. {
  55. if (!Resolve(uid, ref handsComp, false))
  56. return;
  57. if (!handsComp.Hands.Remove(handName, out var hand))
  58. return;
  59. handsComp.SortedHands.Remove(hand.Name);
  60. TryDrop(uid, hand, null, false, true, handsComp);
  61. if (hand.Container != null)
  62. ContainerSystem.ShutdownContainer(hand.Container);
  63. if (handsComp.ActiveHand == hand)
  64. TrySetActiveHand(uid, handsComp.SortedHands.FirstOrDefault(), handsComp);
  65. RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
  66. Dirty(uid, handsComp);
  67. }
  68. /// <summary>
  69. /// Gets rid of all the entity's hands.
  70. /// </summary>
  71. /// <param name="uid"></param>
  72. /// <param name="handsComp"></param>
  73. public void RemoveHands(EntityUid uid, HandsComponent? handsComp = null)
  74. {
  75. if (!Resolve(uid, ref handsComp))
  76. return;
  77. RemoveHands(uid, EnumerateHands(uid), handsComp);
  78. }
  79. private void RemoveHands(EntityUid uid, IEnumerable<Hand> hands, HandsComponent handsComp)
  80. {
  81. if (!hands.Any())
  82. return;
  83. var hand = hands.First();
  84. RemoveHand(uid, hand.Name, handsComp);
  85. // Repeats it for any additional hands.
  86. RemoveHands(uid, hands, handsComp);
  87. }
  88. private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
  89. {
  90. if (eventArgs.SenderSession.AttachedEntity == null)
  91. return;
  92. TrySetActiveHand(eventArgs.SenderSession.AttachedEntity.Value, msg.HandName);
  93. }
  94. /// <summary>
  95. /// Get any empty hand. Prioritizes the currently active hand.
  96. /// </summary>
  97. public bool TryGetEmptyHand(EntityUid uid, [NotNullWhen(true)] out Hand? emptyHand, HandsComponent? handComp = null)
  98. {
  99. emptyHand = null;
  100. if (!Resolve(uid, ref handComp, false))
  101. return false;
  102. foreach (var hand in EnumerateHands(uid, handComp))
  103. {
  104. if (hand.IsEmpty)
  105. {
  106. emptyHand = hand;
  107. return true;
  108. }
  109. }
  110. return false;
  111. }
  112. public bool TryGetActiveHand(Entity<HandsComponent?> entity, [NotNullWhen(true)] out Hand? hand)
  113. {
  114. if (!Resolve(entity, ref entity.Comp, false))
  115. {
  116. hand = null;
  117. return false;
  118. }
  119. hand = entity.Comp.ActiveHand;
  120. return hand != null;
  121. }
  122. public bool TryGetActiveItem(Entity<HandsComponent?> entity, [NotNullWhen(true)] out EntityUid? item)
  123. {
  124. if (!TryGetActiveHand(entity, out var hand))
  125. {
  126. item = null;
  127. return false;
  128. }
  129. item = hand.HeldEntity;
  130. return item != null;
  131. }
  132. /// <summary>
  133. /// Gets active hand item if relevant otherwise gets the entity itself.
  134. /// </summary>
  135. public EntityUid GetActiveItemOrSelf(Entity<HandsComponent?> entity)
  136. {
  137. if (!TryGetActiveItem(entity, out var item))
  138. {
  139. return entity.Owner;
  140. }
  141. return item.Value;
  142. }
  143. public Hand? GetActiveHand(Entity<HandsComponent?> entity)
  144. {
  145. if (!Resolve(entity, ref entity.Comp))
  146. return null;
  147. return entity.Comp.ActiveHand;
  148. }
  149. public EntityUid? GetActiveItem(Entity<HandsComponent?> entity)
  150. {
  151. return GetActiveHand(entity)?.HeldEntity;
  152. }
  153. /// <summary>
  154. /// Enumerate over hands, starting with the currently active hand.
  155. /// </summary>
  156. public IEnumerable<Hand> EnumerateHands(EntityUid uid, HandsComponent? handsComp = null)
  157. {
  158. if (!Resolve(uid, ref handsComp, false))
  159. yield break;
  160. if (handsComp.ActiveHand != null)
  161. yield return handsComp.ActiveHand;
  162. foreach (var name in handsComp.SortedHands)
  163. {
  164. if (name != handsComp.ActiveHand?.Name)
  165. yield return handsComp.Hands[name];
  166. }
  167. }
  168. /// <summary>
  169. /// Enumerate over held items, starting with the item in the currently active hand (if there is one).
  170. /// </summary>
  171. public IEnumerable<EntityUid> EnumerateHeld(EntityUid uid, HandsComponent? handsComp = null)
  172. {
  173. if (!Resolve(uid, ref handsComp, false))
  174. yield break;
  175. if (handsComp.ActiveHandEntity != null)
  176. yield return handsComp.ActiveHandEntity.Value;
  177. foreach (var name in handsComp.SortedHands)
  178. {
  179. if (name == handsComp.ActiveHand?.Name)
  180. continue;
  181. if (handsComp.Hands[name].HeldEntity is { } held)
  182. yield return held;
  183. }
  184. }
  185. /// <summary>
  186. /// Set the currently active hand and raise hand (de)selection events directed at the held entities.
  187. /// </summary>
  188. /// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
  189. /// not trigger interactions.</returns>
  190. public virtual bool TrySetActiveHand(EntityUid uid, string? name, HandsComponent? handComp = null)
  191. {
  192. if (!Resolve(uid, ref handComp))
  193. return false;
  194. if (name == handComp.ActiveHand?.Name)
  195. return false;
  196. Hand? hand = null;
  197. if (name != null && !handComp.Hands.TryGetValue(name, out hand))
  198. return false;
  199. return SetActiveHand(uid, hand, handComp);
  200. }
  201. /// <summary>
  202. /// Set the currently active hand and raise hand (de)selection events directed at the held entities.
  203. /// </summary>
  204. /// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
  205. /// not trigger interactions.</returns>
  206. public bool SetActiveHand(EntityUid uid, Hand? hand, HandsComponent? handComp = null)
  207. {
  208. if (!Resolve(uid, ref handComp))
  209. return false;
  210. if (hand == handComp.ActiveHand)
  211. return false;
  212. if (handComp.ActiveHand?.HeldEntity is { } held)
  213. RaiseLocalEvent(held, new HandDeselectedEvent(uid));
  214. if (hand == null)
  215. {
  216. handComp.ActiveHand = null;
  217. return true;
  218. }
  219. handComp.ActiveHand = hand;
  220. OnHandSetActive?.Invoke((uid, handComp));
  221. if (hand.HeldEntity != null)
  222. RaiseLocalEvent(hand.HeldEntity.Value, new HandSelectedEvent(uid));
  223. Dirty(uid, handComp);
  224. return true;
  225. }
  226. public bool IsHolding(Entity<HandsComponent?> entity, [NotNullWhen(true)] EntityUid? item)
  227. {
  228. return IsHolding(entity, item, out _, entity);
  229. }
  230. public bool IsHolding(EntityUid uid, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out Hand? inHand, HandsComponent? handsComp = null)
  231. {
  232. inHand = null;
  233. if (entity == null)
  234. return false;
  235. if (!Resolve(uid, ref handsComp, false))
  236. return false;
  237. foreach (var hand in handsComp.Hands.Values)
  238. {
  239. if (hand.HeldEntity == entity)
  240. {
  241. inHand = hand;
  242. return true;
  243. }
  244. }
  245. return false;
  246. }
  247. public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] out Hand? hand,
  248. HandsComponent? hands = null)
  249. {
  250. hand = null;
  251. if (!Resolve(handsUid, ref hands))
  252. return false;
  253. return hands.Hands.TryGetValue(handId, out hand);
  254. }
  255. public int CountFreeableHands(Entity<HandsComponent> hands)
  256. {
  257. var freeable = 0;
  258. foreach (var hand in hands.Comp.Hands.Values)
  259. {
  260. if (hand.IsEmpty || CanDropHeld(hands, hand))
  261. freeable++;
  262. }
  263. return freeable;
  264. }
  265. /// <summary>
  266. /// Shitmed Change: This function checks when adding a hand for symmetries to determine where to add it in the sorted hands array.
  267. /// </summary>
  268. /// <param name="handsComp">The hands component that we're modifying.</param>
  269. /// <param name="handName">The name of the hand we're adding.</param>
  270. /// <param name="handLocation">The location/symmetry of the hand we're adding.</param>
  271. public virtual void AddToSortedHands(HandsComponent handsComp, string handName, HandLocation handLocation)
  272. {
  273. var index = handLocation == HandLocation.Right
  274. ? 0
  275. : handLocation == HandLocation.Left
  276. ? handsComp.SortedHands.Count
  277. : handsComp.SortedHands.FindIndex(name => handsComp.Hands[name].Location == HandLocation.Right);
  278. if (index == -1)
  279. index = handsComp.SortedHands.Count;
  280. handsComp.SortedHands.Insert(index, handName);
  281. }
  282. }