1
0

ProximityDetectionSystem.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using Content.Shared.Item.ItemToggle;
  2. using Content.Shared.Item.ItemToggle.Components;
  3. using Content.Shared.ProximityDetection.Components;
  4. using Content.Shared.Tag;
  5. using Robust.Shared.Network;
  6. namespace Content.Shared.ProximityDetection.Systems;
  7. //This handles generic proximity detector logic
  8. public sealed class ProximityDetectionSystem : EntitySystem
  9. {
  10. [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
  11. [Dependency] private readonly ItemToggleSystem _toggle = default!;
  12. [Dependency] private readonly SharedTransformSystem _transform = default!;
  13. [Dependency] private readonly TagSystem _tagSystem = default!;
  14. [Dependency] private readonly INetManager _net = default!;
  15. //update is only run on the server
  16. public override void Initialize()
  17. {
  18. base.Initialize();
  19. SubscribeLocalEvent<ProximityDetectorComponent, ComponentInit>(OnCompInit);
  20. SubscribeLocalEvent<ProximityDetectorComponent, ItemToggledEvent>(OnToggled);
  21. }
  22. private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, ComponentInit args)
  23. {
  24. if (component.Criteria.RequireAll)
  25. return;
  26. Log.Debug("DetectorComponent only supports requireAll = false for tags. All components are required for a match!");
  27. }
  28. public override void Update(float frameTime)
  29. {
  30. if (_net.IsClient)
  31. return;
  32. var query = EntityQueryEnumerator<ProximityDetectorComponent>();
  33. while (query.MoveNext(out var owner, out var detector))
  34. {
  35. if (!_toggle.IsActivated(owner))
  36. continue;
  37. detector.AccumulatedFrameTime += frameTime;
  38. if (detector.AccumulatedFrameTime < detector.UpdateRate)
  39. continue;
  40. detector.AccumulatedFrameTime -= detector.UpdateRate;
  41. RunUpdate_Internal(owner, detector);
  42. }
  43. }
  44. private void OnToggled(Entity<ProximityDetectorComponent> ent, ref ItemToggledEvent args)
  45. {
  46. if (args.Activated)
  47. {
  48. RunUpdate_Internal(ent, ent.Comp);
  49. return;
  50. }
  51. var noDetectEvent = new ProximityTargetUpdatedEvent(ent.Comp, Target: null, ent.Comp.Distance);
  52. RaiseLocalEvent(ent, ref noDetectEvent);
  53. ent.Comp.AccumulatedFrameTime = 0;
  54. Dirty(ent, ent.Comp);
  55. }
  56. public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null)
  57. {
  58. if (!Resolve(owner, ref detector))
  59. return;
  60. RunUpdate_Internal(owner, detector);
  61. }
  62. private void ClearTarget(Entity<ProximityDetectorComponent> ent)
  63. {
  64. var (uid, comp) = ent;
  65. if (comp.TargetEnt == null)
  66. return;
  67. comp.Distance = -1;
  68. comp.TargetEnt = null;
  69. var noDetectEvent = new ProximityTargetUpdatedEvent(comp, null, -1);
  70. RaiseLocalEvent(uid, ref noDetectEvent);
  71. var newTargetEvent = new NewProximityTargetEvent(comp, null);
  72. RaiseLocalEvent(uid, ref newTargetEvent);
  73. Dirty(uid, comp);
  74. }
  75. private void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector)
  76. {
  77. if (!_net.IsServer) //only run detection checks on the server!
  78. return;
  79. if (Deleted(detector.TargetEnt))
  80. {
  81. ClearTarget((owner, detector));
  82. }
  83. var xformQuery = GetEntityQuery<TransformComponent>();
  84. var xform = xformQuery.GetComponent(owner);
  85. List<(EntityUid TargetEnt, float Distance)> detections = new();
  86. if (detector.Criteria.Components == null)
  87. {
  88. Log.Error($"ProximityDetectorComponent on {ToPrettyString(owner)} must use at least 1 component as a filter in criteria!");
  89. throw new ArgumentException($"ProximityDetectorComponent on {ToPrettyString(owner)} must use at least 1 component as a filter in criteria!");
  90. }
  91. var firstCompType = EntityManager.ComponentFactory.GetRegistration(detector.Criteria.Components[0]).Type;
  92. var foundEnts = _entityLookup.GetEntitiesInRange(firstCompType,_transform.GetMapCoordinates(owner, xform), detector.Range.Float());
  93. var tagSearchEnabled = detector.Criteria.Tags is {Count: > 0};
  94. CheckForAllComponentsPresent(detector, ref foundEnts, tagSearchEnabled);
  95. if (foundEnts.Count == 0)
  96. {
  97. UpdateTargetFromClosest(owner, detector, detections);
  98. return;
  99. }
  100. foreach (var ent in foundEnts)
  101. {
  102. if (tagSearchEnabled && ent.Comp is TagComponent tags && (detector.Criteria.RequireAll
  103. ? _tagSystem.HasAllTags(tags, detector.Criteria.Tags!)
  104. : _tagSystem.HasAnyTag(tags, detector.Criteria.Tags!)))
  105. continue;
  106. var distance = (_transform.GetWorldPosition(xform, xformQuery) - _transform.GetWorldPosition(ent, xformQuery)).Length();
  107. if (CheckDetectConditions(ent, distance, owner, detector))
  108. {
  109. detections.Add((ent, distance));
  110. }
  111. }
  112. UpdateTargetFromClosest(owner, detector, detections);
  113. }
  114. private void CheckForAllComponentsPresent(ProximityDetectorComponent detector, ref HashSet<Entity<IComponent>> foundEnts, bool tagSearchEnabled)
  115. {
  116. var validEnts = new HashSet<Entity<IComponent>>(foundEnts.Count);
  117. for (var i = 1; i < detector.Criteria.Components!.Length; i++)
  118. {
  119. validEnts.Clear();
  120. var compType = EntityManager.ComponentFactory.GetRegistration(detector.Criteria.Components[i]).Type;
  121. foreach (var ent in foundEnts)
  122. {
  123. if (!HasComp(ent, compType))
  124. continue;
  125. validEnts.Add(ent);
  126. }
  127. (foundEnts, validEnts) = (validEnts, foundEnts);
  128. }
  129. validEnts.Clear();
  130. if (tagSearchEnabled)
  131. {
  132. foreach (var ent in foundEnts)
  133. {
  134. if (!HasComp<TagComponent>(ent))
  135. continue;
  136. validEnts.Add(ent);
  137. }
  138. (foundEnts, validEnts) = (validEnts, foundEnts);
  139. validEnts.Clear();
  140. }
  141. }
  142. private bool CheckDetectConditions(EntityUid targetEntity, float dist, EntityUid owner, ProximityDetectorComponent detector)
  143. {
  144. var detectAttempt = new ProximityDetectionAttemptEvent(false, dist, (owner, detector));
  145. RaiseLocalEvent(targetEntity, ref detectAttempt);
  146. return !detectAttempt.Cancel;
  147. }
  148. private void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent detector, List<(EntityUid TargetEnt, float Distance)> detections)
  149. {
  150. if (detections.Count == 0)
  151. {
  152. ClearTarget((owner, detector));
  153. return;
  154. }
  155. var closestDistance = detections[0].Distance;
  156. EntityUid closestEnt = default!;
  157. foreach (var (ent,dist) in detections)
  158. {
  159. if (dist >= closestDistance)
  160. continue;
  161. closestEnt = ent;
  162. closestDistance = dist;
  163. }
  164. var newTarget = detector.TargetEnt != closestEnt;
  165. var newData = newTarget || detector.Distance != closestDistance;
  166. detector.TargetEnt = closestEnt;
  167. detector.Distance = closestDistance;
  168. Dirty(owner, detector);
  169. if (newTarget)
  170. {
  171. var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt);
  172. RaiseLocalEvent(owner, ref newTargetEvent);
  173. }
  174. if (!newData)
  175. return;
  176. var targetUpdatedEvent = new ProximityTargetUpdatedEvent(detector, closestEnt, closestDistance);
  177. RaiseLocalEvent(owner, ref targetUpdatedEvent);
  178. Dirty(owner, detector);
  179. }
  180. public void SetRange(EntityUid owner, float newRange, ProximityDetectorComponent? detector = null)
  181. {
  182. if (!Resolve(owner, ref detector))
  183. return;
  184. detector.Range = newRange;
  185. Dirty(owner, detector);
  186. }
  187. }