LinkedEntitySystem.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Linq;
  3. using Content.Shared.Teleportation.Components;
  4. namespace Content.Shared.Teleportation.Systems;
  5. /// <summary>
  6. /// Handles symmetrically linking two entities together, and removing links properly.
  7. /// This does not do anything on its own (outside of deleting entities that have 0 links, if that option is true)
  8. /// Systems can do whatever they please with the linked entities, such as <see cref="SharedPortalSystem"/>.
  9. /// </summary>
  10. public sealed class LinkedEntitySystem : EntitySystem
  11. {
  12. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  13. /// <inheritdoc/>
  14. public override void Initialize()
  15. {
  16. base.Initialize();
  17. SubscribeLocalEvent<LinkedEntityComponent, ComponentShutdown>(OnLinkShutdown);
  18. }
  19. private void OnLinkShutdown(EntityUid uid, LinkedEntityComponent component, ComponentShutdown args)
  20. {
  21. // Remove any links to this entity when deleted.
  22. foreach (var ent in component.LinkedEntities.ToArray())
  23. {
  24. if (!Deleted(ent) && LifeStage(ent) < EntityLifeStage.Terminating && TryComp<LinkedEntityComponent>(ent, out var link))
  25. {
  26. TryUnlink(uid, ent, component, link);
  27. }
  28. }
  29. }
  30. #region Public API
  31. /// <summary>
  32. /// Links two entities together. Does not require the existence of <see cref="LinkedEntityComponent"/> on either
  33. /// already. Linking is symmetrical, so order doesn't matter.
  34. /// </summary>
  35. /// <param name="first">The first entity to link</param>
  36. /// <param name="second">The second entity to link</param>
  37. /// <param name="deleteOnEmptyLinks">Whether both entities should now delete once their links are removed</param>
  38. /// <returns>Whether linking was successful (e.g. they weren't already linked)</returns>
  39. public bool TryLink(EntityUid first, EntityUid second, bool deleteOnEmptyLinks=false)
  40. {
  41. var firstLink = EnsureComp<LinkedEntityComponent>(first);
  42. var secondLink = EnsureComp<LinkedEntityComponent>(second);
  43. firstLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
  44. secondLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
  45. _appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, true);
  46. _appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, true);
  47. Dirty(first, firstLink);
  48. Dirty(second, secondLink);
  49. return firstLink.LinkedEntities.Add(second)
  50. && secondLink.LinkedEntities.Add(first);
  51. }
  52. /// <summary>
  53. /// Does a one-way link from source to target.
  54. /// </summary>
  55. /// <param name="deleteOnEmptyLinks">Whether both entities should now delete once their links are removed</param>
  56. public bool OneWayLink(EntityUid source, EntityUid target, bool deleteOnEmptyLinks=false)
  57. {
  58. var firstLink = EnsureComp<LinkedEntityComponent>(source);
  59. firstLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
  60. _appearance.SetData(source, LinkedEntityVisuals.HasAnyLinks, true);
  61. Dirty(source, firstLink);
  62. return firstLink.LinkedEntities.Add(target);
  63. }
  64. /// <summary>
  65. /// Unlinks two entities. Deletes either entity if <see cref="LinkedEntityComponent.DeleteOnEmptyLinks"/>
  66. /// was true and its links are now empty. Symmetrical, so order doesn't matter.
  67. /// </summary>
  68. /// <param name="first">The first entity to unlink</param>
  69. /// <param name="second">The second entity to unlink</param>
  70. /// <param name="firstLink">Resolve comp</param>
  71. /// <param name="secondLink">Resolve comp</param>
  72. /// <returns>Whether unlinking was successful (e.g. they both were actually linked to one another)</returns>
  73. public bool TryUnlink(EntityUid first, EntityUid second,
  74. LinkedEntityComponent? firstLink = null, LinkedEntityComponent? secondLink = null)
  75. {
  76. if (!Resolve(first, ref firstLink))
  77. return false;
  78. if (!Resolve(second, ref secondLink))
  79. return false;
  80. var success = firstLink.LinkedEntities.Remove(second)
  81. && secondLink.LinkedEntities.Remove(first);
  82. _appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, firstLink.LinkedEntities.Any());
  83. _appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, secondLink.LinkedEntities.Any());
  84. Dirty(first, firstLink);
  85. Dirty(second, secondLink);
  86. if (firstLink.LinkedEntities.Count == 0 && firstLink.DeleteOnEmptyLinks)
  87. QueueDel(first);
  88. if (secondLink.LinkedEntities.Count == 0 && secondLink.DeleteOnEmptyLinks)
  89. QueueDel(second);
  90. return success;
  91. }
  92. /// <summary>
  93. /// Get the first entity this entity is linked to.
  94. /// If multiple are linked only the first one is picked.
  95. /// </summary>
  96. public bool GetLink(EntityUid uid, [NotNullWhen(true)] out EntityUid? dest, LinkedEntityComponent? comp = null)
  97. {
  98. dest = null;
  99. if (!Resolve(uid, ref comp, false))
  100. return false;
  101. var first = comp.LinkedEntities.FirstOrDefault();
  102. if (first != default)
  103. {
  104. dest = first;
  105. return true;
  106. }
  107. return false;
  108. }
  109. #endregion
  110. }