+++ /dev/null
-using Robust.Shared.Audio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-
-namespace Content.Server.Pinpointer;
-
-/// <summary>
-/// This is used for an item that beeps based on
-/// proximity to a specified component.
-/// </summary>
-[RegisterComponent, Access(typeof(ProximityBeeperSystem))]
-public sealed partial class ProximityBeeperComponent : Component
-{
- /// <summary>
- /// Whether or not it's on.
- /// </summary>
- [DataField("enabled")]
- public bool Enabled;
-
- /// <summary>
- /// The target component that is being searched for
- /// </summary>
- [DataField("component", required: true), ViewVariables(VVAccess.ReadWrite)]
- public string Component = default!;
-
- /// <summary>
- /// The farthest distance a target can be for the beep to occur
- /// </summary>
- [DataField("maximumDistance"), ViewVariables(VVAccess.ReadWrite)]
- public float MaximumDistance = 10f;
-
- /// <summary>
- /// The maximum interval between beeps.
- /// </summary>
- [DataField("maxBeepInterval"), ViewVariables(VVAccess.ReadWrite)]
- public TimeSpan MaxBeepInterval = TimeSpan.FromSeconds(1.5f);
-
- /// <summary>
- /// The minimum interval between beeps.
- /// </summary>
- [DataField("minBeepInterval"), ViewVariables(VVAccess.ReadWrite)]
- public TimeSpan MinBeepInterval = TimeSpan.FromSeconds(0.25f);
-
- /// <summary>
- /// When the next beep will occur
- /// </summary>
- [DataField("nextBeepTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
- public TimeSpan NextBeepTime;
-
- /// <summary>
- /// The sound played when the locator beeps.
- /// </summary>
- [DataField("beepSound")]
- public SoundSpecifier? BeepSound;
-}
+++ /dev/null
-using Content.Server.PowerCell;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Pinpointer;
-using Content.Shared.PowerCell;
-using Robust.Server.Audio;
-using Robust.Server.GameObjects;
-using Robust.Shared.Timing;
-
-namespace Content.Server.Pinpointer;
-
-/// <summary>
-/// This handles logic and interaction relating to <see cref="ProximityBeeperComponent"/>
-/// </summary>
-public sealed class ProximityBeeperSystem : EntitySystem
-{
- [Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly AppearanceSystem _appearance = default!;
- [Dependency] private readonly AudioSystem _audio = default!;
- [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
- [Dependency] private readonly PowerCellSystem _powerCell = default!;
- [Dependency] private readonly TransformSystem _transform = default!;
-
- /// <inheritdoc/>
- public override void Initialize()
- {
- SubscribeLocalEvent<ProximityBeeperComponent, UseInHandEvent>(OnUseInHand);
- SubscribeLocalEvent<ProximityBeeperComponent, EntityUnpausedEvent>(OnUnpaused);
- SubscribeLocalEvent<ProximityBeeperComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
- }
- private void OnUseInHand(EntityUid uid, ProximityBeeperComponent component, UseInHandEvent args)
- {
- if (args.Handled)
- return;
-
- args.Handled = TryToggle(uid, component, args.User);
- }
-
- private void OnUnpaused(EntityUid uid, ProximityBeeperComponent component, ref EntityUnpausedEvent args)
- {
- component.NextBeepTime += args.PausedTime;
- }
-
- private void OnPowerCellSlotEmpty(EntityUid uid, ProximityBeeperComponent component, ref PowerCellSlotEmptyEvent args)
- {
- if (component.Enabled)
- TryDisable(uid, component);
- }
-
- /// <summary>
- /// Beeps the proximitybeeper as well as sets the time for the next beep
- /// based on proximity to entities with the target component.
- /// </summary>
- public void UpdateBeep(EntityUid uid, ProximityBeeperComponent? component = null, bool playBeep = true)
- {
- if (!Resolve(uid, ref component))
- return;
-
- if (!component.Enabled)
- {
- component.NextBeepTime += component.MaxBeepInterval;
- return;
- }
-
- var xformQuery = GetEntityQuery<TransformComponent>();
- var xform = xformQuery.GetComponent(uid);
- var compType = EntityManager.ComponentFactory.GetRegistration(component.Component).Type;
- float? closestDistance = null;
- foreach (var ent in _entityLookup.GetEntitiesInRange(compType, xform.MapPosition, component.MaximumDistance))
- {
- var dist = (_transform.GetWorldPosition(xform, xformQuery) - _transform.GetWorldPosition(ent, xformQuery)).Length();
- if (dist >= (closestDistance ?? float.MaxValue))
- continue;
- closestDistance = dist;
- }
-
- if (closestDistance is not { } distance)
- return;
-
- if (playBeep)
- _audio.PlayPvs(component.BeepSound, uid);
-
- var scalingFactor = distance / component.MaximumDistance;
- var interval = (component.MaxBeepInterval - component.MinBeepInterval) * scalingFactor + component.MinBeepInterval;
-
- component.NextBeepTime += interval;
- if (component.NextBeepTime < _timing.CurTime) // Prevents spending time out of range accumulating a deficit which causes a series of very rapid beeps when comeing into range.
- component.NextBeepTime = _timing.CurTime + interval;
- }
-
- /// <summary>
- /// Enables the proximity beeper
- /// </summary>
- public bool TryEnable(EntityUid uid, ProximityBeeperComponent? component = null, EntityUid? user = null)
- {
- if (!Resolve(uid, ref component))
- return false;
-
- TryComp<PowerCellDrawComponent>(uid, out var draw);
-
- if (!_powerCell.HasActivatableCharge(uid, battery: draw, user: user))
- return false;
-
- component.Enabled = true;
- _appearance.SetData(uid, ProximityBeeperVisuals.Enabled, true);
- component.NextBeepTime = _timing.CurTime;
- UpdateBeep(uid, component, false);
- if (draw != null)
- _powerCell.SetPowerCellDrawEnabled(uid, true, draw);
- return true;
- }
-
- /// <summary>
- /// Disables the proximity beeper
- /// </summary>
- public bool TryDisable(EntityUid uid, ProximityBeeperComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return false;
-
- if (!component.Enabled)
- return false;
-
- component.Enabled = false;
- _appearance.SetData(uid, ProximityBeeperVisuals.Enabled, false);
- _powerCell.SetPowerCellDrawEnabled(uid, false);
- UpdateBeep(uid, component);
- return true;
- }
-
- /// <summary>
- /// toggles the proximity beeper
- /// </summary>
- public bool TryToggle(EntityUid uid, ProximityBeeperComponent? component = null, EntityUid? user = null)
- {
- if (!Resolve(uid, ref component))
- return false;
-
- return component.Enabled
- ? TryDisable(uid, component)
- : TryEnable(uid, component, user);
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- var query = EntityQueryEnumerator<ProximityBeeperComponent>();
- while (query.MoveNext(out var uid, out var beeper))
- {
- if (!beeper.Enabled)
- continue;
-
- if (_timing.CurTime < beeper.NextBeepTime)
- continue;
- UpdateBeep(uid, beeper);
- }
- }
-}
--- /dev/null
+namespace Content.Shared.Beeper;
+[ByRefEvent]
+public record struct BeepPlayedEvent(bool Muted);
+
--- /dev/null
+using Content.Shared.Beeper.Systems;
+using Content.Shared.FixedPoint;
+using Content.Shared.ProximityDetection.Systems;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Beeper.Components;
+
+/// <summary>
+/// This is used for an item that beeps based on
+/// proximity to a specified component.
+/// </summary>
+[RegisterComponent, NetworkedComponent, Access(typeof(BeeperSystem)), AutoGenerateComponentState]
+public sealed partial class BeeperComponent : Component
+{
+ /// <summary>
+ /// Whether or not it's on.
+ /// </summary>
+ [DataField("enabled")]
+ public bool Enabled = true;
+
+ /// <summary>
+ /// How much to scale the interval by (< 0 = min, > 1 = max)
+ /// </summary>
+ [DataField("intervalScaling"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public FixedPoint2 IntervalScaling = 0;
+
+ /// <summary>
+ /// The maximum interval between beeps.
+ /// </summary>
+ [DataField("maxBeepInterval"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public TimeSpan MaxBeepInterval = TimeSpan.FromSeconds(1.5f);
+
+ /// <summary>
+ /// The minimum interval between beeps.
+ /// </summary>
+ [DataField("minBeepInterval"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public TimeSpan MinBeepInterval = TimeSpan.FromSeconds(0.25f);
+
+ /// <summary>
+ /// Interval for the next beep
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan Interval;
+
+ /// <summary>
+ /// Time when we beeped last
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan LastBeepTime;
+
+ [ViewVariables(VVAccess.ReadOnly)]
+ public TimeSpan NextBeep => LastBeepTime == TimeSpan.MaxValue ? TimeSpan.MaxValue : LastBeepTime + Interval;
+
+ /// <summary>
+ /// Is the beep muted
+ /// </summary>
+ [DataField("muted"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public bool IsMuted = false;
+
+ /// <summary>
+ /// The sound played when the locator beeps.
+ /// </summary>
+ [DataField("beepSound"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public SoundSpecifier? BeepSound;
+}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Beeper.Components;
+
+[RegisterComponent] //component tag for events. If we add support for component pairs on events then this won't be needed anymore!
+public sealed partial class ProximityBeeperComponent : Component
+{
+}
--- /dev/null
+using Content.Shared.Beeper.Components;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Network;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Beeper.Systems;
+
+
+//This handles generic proximity beeper logic
+public sealed class BeeperSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly INetManager _net = default!;
+
+ public override void Initialize()
+ {
+ }
+
+ public override void Update(float frameTime)
+ {
+ var query = EntityQueryEnumerator<BeeperComponent>();
+ while (query.MoveNext(out var uid, out var beeper))
+ {
+ if (!beeper.Enabled)
+ continue;
+ RunUpdate_Internal(uid, beeper);
+ }
+ }
+
+ public void SetEnable(EntityUid owner, bool isEnabled, BeeperComponent? beeper = null)
+ {
+ if (!Resolve(owner, ref beeper) || beeper.Enabled == isEnabled)
+ return;
+ beeper.Enabled = isEnabled;
+
+ RunUpdate_Internal(owner, beeper);
+ Dirty(owner, beeper);
+ }
+
+ public void SetIntervalScaling(EntityUid owner, BeeperComponent beeper, FixedPoint2 newScaling)
+ {
+ newScaling = FixedPoint2.Clamp(newScaling, 0, 1);
+ beeper.IntervalScaling = newScaling;
+ RunUpdate_Internal(owner, beeper);
+ Dirty(owner, beeper);
+ }
+
+ public void SetInterval(EntityUid owner, BeeperComponent beeper, TimeSpan newInterval)
+ {
+ if (newInterval < beeper.MinBeepInterval)
+ newInterval = beeper.MinBeepInterval;
+ if (newInterval > beeper.MaxBeepInterval)
+ newInterval = beeper.MaxBeepInterval;
+ beeper.Interval = newInterval;
+ RunUpdate_Internal(owner, beeper);
+ Dirty(owner, beeper);
+ }
+
+ public void SetIntervalScaling(EntityUid owner, FixedPoint2 newScaling, BeeperComponent? beeper = null)
+ {
+ if (!Resolve(owner, ref beeper))
+ return;
+ SetIntervalScaling(owner, beeper, newScaling);
+ }
+
+ public void SetMute(EntityUid owner, bool isMuted, BeeperComponent? comp = null)
+ {
+ if (!Resolve(owner, ref comp))
+ return;
+ comp.IsMuted = isMuted;
+ }
+
+ private void UpdateBeepInterval(EntityUid owner, BeeperComponent beeper)
+ {
+ var scalingFactor = beeper.IntervalScaling.Float();
+ var interval = (beeper.MaxBeepInterval - beeper.MinBeepInterval) * scalingFactor + beeper.MinBeepInterval;
+ if (beeper.Interval == interval)
+ return;
+ beeper.Interval = interval;
+ Dirty(owner, beeper);
+ }
+
+ public void ForceUpdate(EntityUid owner, BeeperComponent? beeper = null)
+ {
+ if (!Resolve(owner, ref beeper))
+ return;
+ RunUpdate_Internal(owner, beeper);
+ }
+
+ private void RunUpdate_Internal(EntityUid owner, BeeperComponent beeper)
+ {
+ if (!beeper.Enabled)
+ {
+ return;
+ }
+ UpdateBeepInterval(owner, beeper);
+ if (beeper.NextBeep >= _timing.CurTime)
+ return;
+ var beepEvent = new BeepPlayedEvent(beeper.IsMuted);
+ RaiseLocalEvent(owner, ref beepEvent);
+ if (!beeper.IsMuted && _net.IsServer)
+ {
+ _audio.PlayPvs(beeper.BeepSound, owner);
+ }
+ beeper.LastBeepTime = _timing.CurTime;
+ }
+}
--- /dev/null
+using Content.Shared.Beeper.Components;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Pinpointer;
+using Content.Shared.PowerCell;
+using Content.Shared.ProximityDetection;
+using Content.Shared.ProximityDetection.Components;
+using Content.Shared.ProximityDetection.Systems;
+
+namespace Content.Shared.Beeper.Systems;
+
+/// <summary>
+/// This handles logic for implementing proximity beeper as a handheld tool />
+/// </summary>
+public sealed class ProximityBeeperSystem : EntitySystem
+{
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly SharedPowerCellSystem _powerCell = default!;
+ [Dependency] private readonly ProximityDetectionSystem _proximity = default!;
+ [Dependency] private readonly BeeperSystem _beeper = default!;
+
+ /// <inheritdoc/>
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<ProximityBeeperComponent, UseInHandEvent>(OnUseInHand);
+ SubscribeLocalEvent<ProximityBeeperComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
+ SubscribeLocalEvent<ProximityBeeperComponent, NewProximityTargetEvent>(OnNewProximityTarget);
+ SubscribeLocalEvent<ProximityBeeperComponent, ProximityTargetUpdatedEvent>(OnProximityTargetUpdate);
+ }
+
+ private void OnProximityTargetUpdate(EntityUid owner, ProximityBeeperComponent proxBeeper, ref ProximityTargetUpdatedEvent args)
+ {
+ if (!TryComp<BeeperComponent>(owner, out var beeper))
+ return;
+ if (args.Target == null)
+ {
+ _beeper.SetEnable(owner, false, beeper);
+ return;
+ }
+ _beeper.SetIntervalScaling(owner,args.Distance/args.Detector.Range, beeper);
+ _beeper.SetEnable(owner, true, beeper);
+ }
+
+ private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args)
+ {
+ _beeper.SetEnable(owner, args.Target != null);
+ }
+
+ private void OnUseInHand(EntityUid uid, ProximityBeeperComponent proxBeeper, UseInHandEvent args)
+ {
+ if (args.Handled)
+ return;
+ args.Handled = TryToggle(uid, proxBeeper, user: args.User);
+ }
+
+ private void OnPowerCellSlotEmpty(EntityUid uid, ProximityBeeperComponent beeper, ref PowerCellSlotEmptyEvent args)
+ {
+ if (_proximity.GetEnable(uid))
+ TryDisable(uid);
+ }
+ public bool TryEnable(EntityUid owner, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null,
+ PowerCellDrawComponent? draw = null,EntityUid? user = null)
+ {
+ if (!Resolve(owner, ref beeper, ref detector))
+ return false;
+ if (Resolve(owner, ref draw, false) && !_powerCell.HasActivatableCharge(owner, battery: draw, user: user))
+ return false;
+ Enable(owner, beeper, detector, draw);
+ return true;
+ }
+ private void Enable(EntityUid owner, BeeperComponent beeper,
+ ProximityDetectorComponent detector, PowerCellDrawComponent? draw)
+ {
+ _proximity.SetEnable(owner, true, detector);
+ _appearance.SetData(owner, ProximityBeeperVisuals.Enabled, true);
+ _powerCell.SetPowerCellDrawEnabled(owner, true, draw);
+ }
+
+
+ /// <summary>
+ /// Disables the proximity beeper
+ /// </summary>
+ public bool TryDisable(EntityUid owner,BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, PowerCellDrawComponent? draw = null)
+ {
+ if (!Resolve(owner, ref beeper, ref detector))
+ return false;
+
+ if (!detector.Enabled)
+ return false;
+ Disable(owner, beeper, detector, draw);
+ return true;
+ }
+ private void Disable(EntityUid owner, BeeperComponent beeper,
+ ProximityDetectorComponent detector, PowerCellDrawComponent? draw)
+ {
+ _proximity.SetEnable(owner, false, detector);
+ _appearance.SetData(owner, ProximityBeeperVisuals.Enabled, false);
+ _beeper.SetEnable(owner, false, beeper);
+ _powerCell.SetPowerCellDrawEnabled(owner, false, draw);
+ }
+
+ /// <summary>
+ /// toggles the proximity beeper
+ /// </summary>
+ public bool TryToggle(EntityUid owner, ProximityBeeperComponent? proxBeeper = null, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null,
+ PowerCellDrawComponent? draw = null, EntityUid? user = null)
+ {
+ if (!Resolve(owner, ref proxBeeper, ref beeper, ref detector))
+ return false;
+
+ return detector.Enabled
+ ? TryDisable(owner, beeper, detector, draw)
+ : TryEnable(owner, beeper, detector, draw,user);
+ }
+}
--- /dev/null
+using Content.Shared.FixedPoint;
+using Content.Shared.ProximityDetection.Systems;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.ProximityDetection.Components;
+/// <summary>
+/// This is used to search for the closest entity with a range that matches specified requirements (tags and/or components)
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState ,Access(typeof(ProximityDetectionSystem))]
+public sealed partial class ProximityDetectorComponent : Component
+{
+ /// <summary>
+ /// Whether or not it's on.
+ /// </summary>
+ [DataField("enabled"), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public bool Enabled = true;
+
+ [DataField("criteria", required: true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
+ public EntityWhitelist Criteria = default!;
+
+ /// <summary>
+ /// Found Entity
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public EntityUid? TargetEnt;
+
+ /// <summary>
+ /// Distance to Found Entity
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public FixedPoint2 Distance = -1;
+
+
+ /// <summary>
+ /// The farthest distance to search for targets
+ /// </summary>
+ [DataField("range"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public FixedPoint2 Range = 10f;
+
+ public float AccumulatedFrameTime;
+
+ [DataField("updateRate"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public float UpdateRate = 0.3f;
+}
--- /dev/null
+using Content.Shared.FixedPoint;
+using Content.Shared.ProximityDetection.Components;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.ProximityDetection;
+
+[ByRefEvent]
+public record struct ProximityDetectionAttemptEvent(bool Cancel, FixedPoint2 Distance, Entity<ProximityDetectorComponent> Detector);
+
+[ByRefEvent]
+public record struct ProximityTargetUpdatedEvent(ProximityDetectorComponent Detector, EntityUid? Target, FixedPoint2 Distance);
+
+[ByRefEvent]
+public record struct NewProximityTargetEvent(ProximityDetectorComponent Detector, EntityUid? Target);
+
+
+
--- /dev/null
+using Content.Shared.ProximityDetection.Components;
+using Robust.Shared.Network;
+
+namespace Content.Shared.ProximityDetection.Systems;
+
+
+//This handles generic proximity detector logic
+public sealed class ProximityDetectionSystem : EntitySystem
+{
+ [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly INetManager _net = default!;
+
+ //update is only run on the server
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<ProximityDetectorComponent, EntityPausedEvent>(OnPaused);
+ SubscribeLocalEvent<ProximityDetectorComponent, EntityUnpausedEvent>(OnUnpaused);
+ }
+
+ protected void OnPaused(EntityUid owner, ProximityDetectorComponent component, EntityPausedEvent args)
+ {
+ SetEnable_Internal(owner,component,false);
+ }
+
+ protected void OnUnpaused(EntityUid owner, ProximityDetectorComponent detector, ref EntityUnpausedEvent args)
+ {
+ SetEnable_Internal(owner, detector,true);
+ }
+ protected internal void SetEnable(EntityUid owner, bool enabled, ProximityDetectorComponent? detector = null)
+ {
+ if (!Resolve(owner, ref detector) || detector.Enabled == enabled)
+ return;
+ SetEnable_Internal(owner ,detector, enabled);
+ }
+
+ public override void Update(float frameTime)
+ {
+ if (_net.IsClient)
+ return;
+ var query = EntityQueryEnumerator<ProximityDetectorComponent>();
+ while (query.MoveNext(out var owner, out var detector))
+ {
+ if (!detector.Enabled)
+ continue;
+ detector.AccumulatedFrameTime += frameTime;
+ if (detector.AccumulatedFrameTime < detector.UpdateRate)
+ continue;
+ detector.AccumulatedFrameTime -= detector.UpdateRate;
+ RunUpdate_Internal(owner, detector);
+ }
+ }
+
+ protected internal bool GetEnable(EntityUid owner, ProximityDetectorComponent? detector = null)
+ {
+ return Resolve(owner, ref detector, false) && detector.Enabled;
+ }
+
+ protected void SetEnable_Internal(EntityUid owner,ProximityDetectorComponent detector, bool enabled)
+ {
+ detector.Enabled = enabled;
+ var noDetectEvent = new ProximityTargetUpdatedEvent(detector, detector.TargetEnt, detector.Distance);
+ RaiseLocalEvent(owner, ref noDetectEvent);
+ if (!enabled)
+ {
+ detector.AccumulatedFrameTime = 0;
+ RunUpdate_Internal(owner, detector);
+ Dirty(owner, detector);
+ return;
+ }
+ RunUpdate_Internal(owner, detector);
+ }
+
+ protected void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null)
+ {
+ if (!Resolve(owner, ref detector))
+ return;
+ RunUpdate_Internal(owner, detector);
+ }
+
+
+ protected void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector)
+ {
+ if (!_net.IsServer) //only run detection checks on the server!
+ return;
+ var xformQuery = GetEntityQuery<TransformComponent>();
+ var xform = xformQuery.GetComponent(owner);
+ List<(EntityUid TargetEnt, float Distance)> detections = new();
+ foreach (var ent in _entityLookup.GetEntitiesInRange(_transform.GetMapCoordinates(owner, xform),
+ detector.Range.Float()))
+ {
+ if (!detector.Criteria.IsValid(ent, EntityManager))
+ continue;
+ var distance = (_transform.GetWorldPosition(xform, xformQuery) - _transform.GetWorldPosition(ent, xformQuery)).Length();
+ if (CheckDetectConditions(ent, distance, owner, detector))
+ {
+ detections.Add((ent, distance));
+ }
+ }
+ UpdateTargetFromClosest(owner, detector, detections);
+ }
+
+ protected bool CheckDetectConditions(EntityUid targetEntity, float dist, EntityUid owner, ProximityDetectorComponent detector)
+ {
+ var detectAttempt = new ProximityDetectionAttemptEvent(false, dist, (owner, detector));
+ RaiseLocalEvent(targetEntity, ref detectAttempt);
+ return !detectAttempt.Cancel;
+ }
+
+ protected void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent detector, List<(EntityUid TargetEnt, float Distance)> detections)
+ {
+ if (detections.Count == 0)
+ {
+ if (detector.TargetEnt == null)
+ return;
+ detector.Distance = -1;
+ detector.TargetEnt = null;
+ var noDetectEvent = new ProximityTargetUpdatedEvent(detector, null, -1);
+ RaiseLocalEvent(owner, ref noDetectEvent);
+ var newTargetEvent = new NewProximityTargetEvent(detector, null);
+ RaiseLocalEvent(owner, ref newTargetEvent);
+ Dirty(owner, detector);
+ return;
+ }
+ var closestDistance = detections[0].Distance;
+ EntityUid closestEnt = default!;
+ foreach (var (ent,dist) in detections)
+ {
+ if (dist >= closestDistance)
+ continue;
+ closestEnt = ent;
+ closestDistance = dist;
+ }
+
+ var newTarget = detector.TargetEnt != closestEnt;
+ var newData = newTarget || detector.Distance != closestDistance;
+ detector.TargetEnt = closestEnt;
+ detector.Distance = closestDistance;
+ if (newTarget)
+ {
+ var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt);
+ RaiseLocalEvent(owner, ref newTargetEvent);
+ }
+
+ if (!newData)
+ return;
+ var targetUpdatedEvent = new ProximityTargetUpdatedEvent(detector, closestEnt, closestDistance);
+ RaiseLocalEvent(owner, ref targetUpdatedEvent);
+ Dirty(owner, detector);
+ }
+
+ public void SetRange(EntityUid owner, float newRange, ProximityDetectorComponent? detector = null)
+ {
+ if (!Resolve(owner, ref detector))
+ return;
+ detector.Range = newRange;
+ Dirty(owner, detector);
+ }
+}
True: { visible: true }
False: { visible: false }
- type: ProximityBeeper
- component: Anomaly
- maximumDistance: 20
+ - type: ProximityDetector
+ enabled: false
+ range: 20
+ criteria:
+ components:
+ - Anomaly
+ - type: Beeper
+ enabled: false
minBeepInterval: 0.15
maxBeepInterval: 1.00
beepSound:
components:
- type: Sprite
sprite: Objects/Specific/Research/anomalylocatorwide.rsi
- - type: ProximityBeeper
- maximumDistance: 40
+ - type: Beeper
minBeepInterval: 0.5
maxBeepInterval: 0.5
-
+ - type: ProximityDetector
+ range: 40
- type: entity
id: AnomalyLocatorWide
parent: [ AnomalyLocatorWideUnpowered, PowerCellSlotSmallItem ]