MechGrabberSystem.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System.Linq;
  2. using Content.Server.Interaction;
  3. using Content.Server.Mech.Equipment.Components;
  4. using Content.Server.Mech.Systems;
  5. using Content.Shared.DoAfter;
  6. using Content.Shared.Interaction;
  7. using Content.Shared.Mech;
  8. using Content.Shared.Mech.Components;
  9. using Content.Shared.Mech.Equipment.Components;
  10. using Content.Shared.Mobs.Components;
  11. using Content.Shared.Wall;
  12. using Robust.Server.GameObjects;
  13. using Robust.Shared.Audio;
  14. using Robust.Shared.Audio.Systems;
  15. using Robust.Shared.Containers;
  16. using Robust.Shared.Map;
  17. using Robust.Shared.Physics;
  18. using Robust.Shared.Physics.Components;
  19. namespace Content.Server.Mech.Equipment.EntitySystems;
  20. /// <summary>
  21. /// Handles <see cref="MechGrabberComponent"/> and all related UI logic
  22. /// </summary>
  23. public sealed class MechGrabberSystem : EntitySystem
  24. {
  25. [Dependency] private readonly SharedContainerSystem _container = default!;
  26. [Dependency] private readonly MechSystem _mech = default!;
  27. [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
  28. [Dependency] private readonly InteractionSystem _interaction = default!;
  29. [Dependency] private readonly SharedAudioSystem _audio = default!;
  30. [Dependency] private readonly TransformSystem _transform = default!;
  31. /// <inheritdoc/>
  32. public override void Initialize()
  33. {
  34. SubscribeLocalEvent<MechGrabberComponent, MechEquipmentUiMessageRelayEvent>(OnGrabberMessage);
  35. SubscribeLocalEvent<MechGrabberComponent, ComponentStartup>(OnStartup);
  36. SubscribeLocalEvent<MechGrabberComponent, MechEquipmentUiStateReadyEvent>(OnUiStateReady);
  37. SubscribeLocalEvent<MechGrabberComponent, MechEquipmentRemovedEvent>(OnEquipmentRemoved);
  38. SubscribeLocalEvent<MechGrabberComponent, AttemptRemoveMechEquipmentEvent>(OnAttemptRemove);
  39. SubscribeLocalEvent<MechGrabberComponent, UserActivateInWorldEvent>(OnInteract);
  40. SubscribeLocalEvent<MechGrabberComponent, GrabberDoAfterEvent>(OnMechGrab);
  41. }
  42. private void OnGrabberMessage(EntityUid uid, MechGrabberComponent component, MechEquipmentUiMessageRelayEvent args)
  43. {
  44. if (args.Message is not MechGrabberEjectMessage msg)
  45. return;
  46. if (!TryComp<MechEquipmentComponent>(uid, out var equipmentComponent) ||
  47. equipmentComponent.EquipmentOwner == null)
  48. return;
  49. var mech = equipmentComponent.EquipmentOwner.Value;
  50. var targetCoords = new EntityCoordinates(mech, component.DepositOffset);
  51. if (!_interaction.InRangeUnobstructed(mech, targetCoords))
  52. return;
  53. var item = GetEntity(msg.Item);
  54. if (!component.ItemContainer.Contains(item))
  55. return;
  56. RemoveItem(uid, mech, item, component);
  57. }
  58. /// <summary>
  59. /// Removes an item from the grabber's container
  60. /// </summary>
  61. /// <param name="uid">The mech grabber</param>
  62. /// <param name="mech">The mech it belongs to</param>
  63. /// <param name="toRemove">The item being removed</param>
  64. /// <param name="component"></param>
  65. public void RemoveItem(EntityUid uid, EntityUid mech, EntityUid toRemove, MechGrabberComponent? component = null)
  66. {
  67. if (!Resolve(uid, ref component))
  68. return;
  69. _container.Remove(toRemove, component.ItemContainer);
  70. var mechxform = Transform(mech);
  71. var xform = Transform(toRemove);
  72. _transform.AttachToGridOrMap(toRemove, xform);
  73. var (mechPos, mechRot) = _transform.GetWorldPositionRotation(mechxform);
  74. var offset = mechPos + mechRot.RotateVec(component.DepositOffset);
  75. _transform.SetWorldPositionRotation(toRemove, offset, Angle.Zero);
  76. _mech.UpdateUserInterface(mech);
  77. }
  78. private void OnEquipmentRemoved(EntityUid uid, MechGrabberComponent component, ref MechEquipmentRemovedEvent args)
  79. {
  80. if (!TryComp<MechEquipmentComponent>(uid, out var equipmentComponent) ||
  81. equipmentComponent.EquipmentOwner == null)
  82. return;
  83. var mech = equipmentComponent.EquipmentOwner.Value;
  84. var allItems = new List<EntityUid>(component.ItemContainer.ContainedEntities);
  85. foreach (var item in allItems)
  86. {
  87. RemoveItem(uid, mech, item, component);
  88. }
  89. }
  90. private void OnAttemptRemove(EntityUid uid, MechGrabberComponent component, ref AttemptRemoveMechEquipmentEvent args)
  91. {
  92. args.Cancelled = component.ItemContainer.ContainedEntities.Any();
  93. }
  94. private void OnStartup(EntityUid uid, MechGrabberComponent component, ComponentStartup args)
  95. {
  96. component.ItemContainer = _container.EnsureContainer<Container>(uid, "item-container");
  97. }
  98. private void OnUiStateReady(EntityUid uid, MechGrabberComponent component, MechEquipmentUiStateReadyEvent args)
  99. {
  100. var state = new MechGrabberUiState
  101. {
  102. Contents = GetNetEntityList(component.ItemContainer.ContainedEntities.ToList()),
  103. MaxContents = component.MaxContents
  104. };
  105. args.States.Add(GetNetEntity(uid), state);
  106. }
  107. private void OnInteract(EntityUid uid, MechGrabberComponent component, UserActivateInWorldEvent args)
  108. {
  109. if (args.Handled)
  110. return;
  111. var target = args.Target;
  112. if (args.Target == args.User || component.DoAfter != null)
  113. return;
  114. if (TryComp<PhysicsComponent>(target, out var physics) && physics.BodyType == BodyType.Static ||
  115. HasComp<WallMountComponent>(target) ||
  116. HasComp<MobStateComponent>(target))
  117. {
  118. return;
  119. }
  120. if (Transform(target).Anchored)
  121. return;
  122. if (component.ItemContainer.ContainedEntities.Count >= component.MaxContents)
  123. return;
  124. if (!TryComp<MechComponent>(args.User, out var mech) || mech.PilotSlot.ContainedEntity == target)
  125. return;
  126. if (mech.Energy + component.GrabEnergyDelta < 0)
  127. return;
  128. if (!_interaction.InRangeUnobstructed(args.User, target))
  129. return;
  130. args.Handled = true;
  131. component.AudioStream = _audio.PlayPvs(component.GrabSound, uid)?.Entity;
  132. var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.GrabDelay, new GrabberDoAfterEvent(), uid, target: target, used: uid)
  133. {
  134. BreakOnMove = true
  135. };
  136. _doAfter.TryStartDoAfter(doAfterArgs, out component.DoAfter);
  137. }
  138. private void OnMechGrab(EntityUid uid, MechGrabberComponent component, DoAfterEvent args)
  139. {
  140. component.DoAfter = null;
  141. if (args.Cancelled)
  142. {
  143. component.AudioStream = _audio.Stop(component.AudioStream);
  144. return;
  145. }
  146. if (args.Handled || args.Args.Target == null)
  147. return;
  148. if (!TryComp<MechEquipmentComponent>(uid, out var equipmentComponent) || equipmentComponent.EquipmentOwner == null)
  149. return;
  150. if (!_mech.TryChangeEnergy(equipmentComponent.EquipmentOwner.Value, component.GrabEnergyDelta))
  151. return;
  152. _container.Insert(args.Args.Target.Value, component.ItemContainer);
  153. _mech.UpdateUserInterface(equipmentComponent.EquipmentOwner.Value);
  154. args.Handled = true;
  155. }
  156. }