HandsSystem.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using System.Numerics;
  2. using Content.Server.Inventory;
  3. using Content.Server.Stack;
  4. using Content.Server.Stunnable;
  5. using Content.Shared.ActionBlocker;
  6. using Content.Shared.Body.Part;
  7. using Content.Shared.CombatMode;
  8. using Content.Shared.Damage.Systems;
  9. using Content.Shared.Explosion;
  10. using Content.Shared.Hands.Components;
  11. using Content.Shared.Hands.EntitySystems;
  12. using Content.Shared.Input;
  13. using Content.Shared.Inventory.VirtualItem;
  14. using Content.Shared.Movement.Pulling.Components;
  15. using Content.Shared.Movement.Pulling.Events;
  16. using Content.Shared.Movement.Pulling.Systems;
  17. using Content.Shared.Stacks;
  18. using Content.Shared.Throwing;
  19. using Robust.Shared.GameStates;
  20. using Robust.Shared.Input.Binding;
  21. using Robust.Shared.Map;
  22. using Robust.Shared.Player;
  23. using Robust.Shared.Random;
  24. using Robust.Shared.Timing;
  25. using Robust.Shared.Utility;
  26. using Content.Shared.Body.Systems; // Shitmed Change
  27. using Content.Shared._Shitmed.Body.Events; // Shitmed Change
  28. namespace Content.Server.Hands.Systems
  29. {
  30. public sealed class HandsSystem : SharedHandsSystem
  31. {
  32. [Dependency] private readonly IGameTiming _timing = default!;
  33. [Dependency] private readonly IRobustRandom _random = default!;
  34. [Dependency] private readonly StackSystem _stackSystem = default!;
  35. [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!;
  36. [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
  37. [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
  38. [Dependency] private readonly PullingSystem _pullingSystem = default!;
  39. [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
  40. [Dependency] private readonly SharedBodySystem _bodySystem = default!; // Shitmed Change
  41. public override void Initialize()
  42. {
  43. base.Initialize();
  44. SubscribeLocalEvent<HandsComponent, DisarmedEvent>(OnDisarmed, before: new[] { typeof(StunSystem), typeof(StaminaSystem) });
  45. SubscribeLocalEvent<HandsComponent, PullStartedMessage>(HandlePullStarted);
  46. SubscribeLocalEvent<HandsComponent, PullStoppedMessage>(HandlePullStopped);
  47. SubscribeLocalEvent<HandsComponent, BodyPartAddedEvent>(HandleBodyPartAdded);
  48. SubscribeLocalEvent<HandsComponent, BodyPartRemovedEvent>(HandleBodyPartRemoved);
  49. SubscribeLocalEvent<HandsComponent, ComponentGetState>(GetComponentState);
  50. SubscribeLocalEvent<HandsComponent, BeforeExplodeEvent>(OnExploded);
  51. SubscribeLocalEvent<HandsComponent, BodyPartEnabledEvent>(HandleBodyPartEnabled); // Shitmed Change
  52. SubscribeLocalEvent<HandsComponent, BodyPartDisabledEvent>(HandleBodyPartDisabled); // Shitmed Change
  53. CommandBinds.Builder
  54. .Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
  55. .Register<HandsSystem>();
  56. }
  57. public override void Shutdown()
  58. {
  59. base.Shutdown();
  60. CommandBinds.Unregister<HandsSystem>();
  61. }
  62. private void GetComponentState(EntityUid uid, HandsComponent hands, ref ComponentGetState args)
  63. {
  64. args.State = new HandsComponentState(hands);
  65. }
  66. private void OnExploded(Entity<HandsComponent> ent, ref BeforeExplodeEvent args)
  67. {
  68. if (ent.Comp.DisableExplosionRecursion)
  69. return;
  70. foreach (var hand in ent.Comp.Hands.Values)
  71. {
  72. if (hand.HeldEntity is { } uid)
  73. args.Contents.Add(uid);
  74. }
  75. }
  76. private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent args)
  77. {
  78. if (args.Handled)
  79. return;
  80. // Break any pulls
  81. if (TryComp(uid, out PullerComponent? puller) && TryComp(puller.Pulling, out PullableComponent? pullable))
  82. _pullingSystem.TryStopPull(puller.Pulling.Value, pullable);
  83. var offsetRandomCoordinates = _transformSystem.GetMoverCoordinates(args.Target).Offset(_random.NextVector2(1f, 1.5f));
  84. if (!ThrowHeldItem(args.Target, offsetRandomCoordinates))
  85. return;
  86. args.PopupPrefix = "disarm-action-";
  87. args.Handled = true; // no shove/stun.
  88. }
  89. // Shitmed Change Start
  90. private void TryAddHand(EntityUid uid, HandsComponent component, Entity<BodyPartComponent> part, string slot)
  91. {
  92. if (part.Comp is null
  93. || part.Comp.PartType != BodyPartType.Hand)
  94. return;
  95. // If this annoys you, which it should.
  96. // Ping Smugleaf.
  97. var location = part.Comp.Symmetry switch
  98. {
  99. BodyPartSymmetry.None => HandLocation.Middle,
  100. BodyPartSymmetry.Left => HandLocation.Left,
  101. BodyPartSymmetry.Right => HandLocation.Right,
  102. _ => throw new ArgumentOutOfRangeException(nameof(part.Comp.Symmetry))
  103. };
  104. if (part.Comp.Enabled
  105. && _bodySystem.TryGetParentBodyPart(part, out var _, out var parentPartComp)
  106. && parentPartComp.Enabled)
  107. AddHand(uid, slot, location);
  108. }
  109. private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args)
  110. {
  111. TryAddHand(uid, component, args.Part, args.Slot);
  112. }
  113. private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args)
  114. {
  115. if (args.Part.Comp is null
  116. || args.Part.Comp.PartType != BodyPartType.Hand)
  117. return;
  118. RemoveHand(uid, args.Slot);
  119. }
  120. private void HandleBodyPartEnabled(EntityUid uid, HandsComponent component, ref BodyPartEnabledEvent args) =>
  121. TryAddHand(uid, component, args.Part, SharedBodySystem.GetPartSlotContainerId(args.Part.Comp.ParentSlot?.Id ?? string.Empty));
  122. private void HandleBodyPartDisabled(EntityUid uid, HandsComponent component, ref BodyPartDisabledEvent args)
  123. {
  124. if (TerminatingOrDeleted(uid)
  125. || args.Part.Comp is null
  126. || args.Part.Comp.PartType != BodyPartType.Hand)
  127. return;
  128. RemoveHand(uid, SharedBodySystem.GetPartSlotContainerId(args.Part.Comp.ParentSlot?.Id ?? string.Empty));
  129. }
  130. // Shitmed Change End
  131. #region pulling
  132. private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
  133. {
  134. if (args.PullerUid != uid)
  135. return;
  136. if (TryComp<PullerComponent>(args.PullerUid, out var pullerComp) && !pullerComp.NeedsHands)
  137. return;
  138. if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.PulledUid, uid))
  139. {
  140. DebugTools.Assert("Unable to find available hand when starting pulling??");
  141. }
  142. }
  143. private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
  144. {
  145. if (args.PullerUid != uid)
  146. return;
  147. // Try find hand that is doing this pull.
  148. // and clear it.
  149. foreach (var hand in component.Hands.Values)
  150. {
  151. if (hand.HeldEntity == null
  152. || !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem)
  153. || virtualItem.BlockingEntity != args.PulledUid)
  154. {
  155. continue;
  156. }
  157. TryDrop(args.PullerUid, hand, handsComp: component);
  158. break;
  159. }
  160. }
  161. #endregion
  162. #region interactions
  163. private bool HandleThrowItem(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
  164. {
  165. if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player) || !coordinates.IsValid(EntityManager))
  166. return false;
  167. return ThrowHeldItem(player, coordinates);
  168. }
  169. /// <summary>
  170. /// Throw the player's currently held item.
  171. /// </summary>
  172. public bool ThrowHeldItem(EntityUid player, EntityCoordinates coordinates, float minDistance = 0.1f)
  173. {
  174. if (ContainerSystem.IsEntityInContainer(player) ||
  175. !TryComp(player, out HandsComponent? hands) ||
  176. hands.ActiveHandEntity is not { } throwEnt ||
  177. !_actionBlockerSystem.CanThrow(player, throwEnt))
  178. return false;
  179. if (_timing.CurTime < hands.NextThrowTime)
  180. return false;
  181. hands.NextThrowTime = _timing.CurTime + hands.ThrowCooldown;
  182. if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually)
  183. {
  184. var splitStack = _stackSystem.Split(throwEnt, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
  185. if (splitStack is not { Valid: true })
  186. return false;
  187. throwEnt = splitStack.Value;
  188. }
  189. var direction = _transformSystem.ToMapCoordinates(coordinates).Position - _transformSystem.GetWorldPosition(player);
  190. if (direction == Vector2.Zero)
  191. return true;
  192. var length = direction.Length();
  193. var distance = Math.Clamp(length, minDistance, hands.ThrowRange);
  194. direction *= distance / length;
  195. var throwSpeed = hands.BaseThrowspeed;
  196. // Let other systems change the thrown entity (useful for virtual items)
  197. // or the throw strength.
  198. var ev = new BeforeThrowEvent(throwEnt, direction, throwSpeed, player);
  199. RaiseLocalEvent(player, ref ev);
  200. if (ev.Cancelled)
  201. return true;
  202. // This can grief the above event so we raise it afterwards
  203. if (IsHolding(player, throwEnt, out _, hands) && !TryDrop(player, throwEnt, handsComp: hands))
  204. return false;
  205. _throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowSpeed, ev.PlayerUid, compensateFriction: !HasComp<LandAtCursorComponent>(ev.ItemUid));
  206. return true;
  207. }
  208. #endregion
  209. }
  210. }