CatStation/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs
ScarKy0 4a88b375df
Make cameras react to the AI eye nearby (#43466)
* init

TODO:
Make this part of the surveillance camera system
Make this be bearable when unpowered(Probably commented out until we make AI lose vision without power)
Remove leftover shit
Probably clean up the camera RSI

* move to surv cameras, unpowered, remove leftovers

* silly

* review

* review

* I can see the light

* reverse fixtures

* review

* dependency query

* unused oops
2026-04-05 15:03:29 +00:00

110 lines
4.2 KiB
C#

using Content.Server.Chat.Systems;
using Content.Shared.Speech;
using Content.Shared.Speech.Components;
using Content.Shared.SurveillanceCamera.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Player;
using static Content.Server.Chat.Systems.ChatSystem;
namespace Content.Server.SurveillanceCamera;
public sealed class SurveillanceCameraMicrophoneSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _xforms = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SurveillanceCameraMicrophoneComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SurveillanceCameraMicrophoneComponent, ListenEvent>(RelayEntityMessage);
SubscribeLocalEvent<SurveillanceCameraMicrophoneComponent, ListenAttemptEvent>(CanListen);
SubscribeLocalEvent<ExpandICChatRecipientsEvent>(OnExpandRecipients);
}
private void OnExpandRecipients(ExpandICChatRecipientsEvent ev)
{
var xformQuery = GetEntityQuery<TransformComponent>();
var sourceXform = Transform(ev.Source);
var sourcePos = _xforms.GetWorldPosition(sourceXform, xformQuery);
// This function ensures that chat popups appear on camera views that have connected microphones.
foreach (var (_, __, camera, xform) in EntityQuery<SurveillanceCameraMicrophoneComponent, ActiveListenerComponent, SurveillanceCameraComponent, TransformComponent>())
{
if (camera.ActivePvsViewers.Count == 0)
continue;
// get range to camera. This way wispers will still appear as obfuscated if they are too far from the camera's microphone
var range = (xform.MapID != sourceXform.MapID)
? -1
: (sourcePos - _xforms.GetWorldPosition(xform, xformQuery)).Length();
if (range < 0 || range > ev.VoiceRange)
continue;
foreach (var viewer in camera.ActivePvsViewers)
{
// if the player has not already received the chat message, send it to them but don't log it to the chat
// window. This is simply so that it appears in camera.
if (TryComp(viewer, out ActorComponent? actor))
ev.Recipients.TryAdd(actor.PlayerSession, new ICChatRecipientData(range, false, true));
}
}
}
private void OnInit(EntityUid uid, SurveillanceCameraMicrophoneComponent component, ComponentInit args)
{
if (component.Enabled)
EnsureComp<ActiveListenerComponent>(uid).Range = component.Range;
else
RemCompDeferred<ActiveListenerComponent>(uid);
}
public void CanListen(EntityUid uid, SurveillanceCameraMicrophoneComponent microphone, ListenAttemptEvent args)
{
// TODO maybe just make this a part of ActiveListenerComponent?
if (_whitelistSystem.IsWhitelistPass(microphone.Blacklist, args.Source))
args.Cancel();
}
public void RelayEntityMessage(EntityUid uid, SurveillanceCameraMicrophoneComponent component, ListenEvent args)
{
if (!TryComp(uid, out SurveillanceCameraComponent? camera))
return;
var ev = new SurveillanceCameraSpeechSendEvent(args.Source, args.Message);
foreach (var monitor in camera.ActiveMonitors)
{
RaiseLocalEvent(monitor, ev);
}
}
public void SetEnabled(EntityUid uid, bool value, SurveillanceCameraMicrophoneComponent? microphone = null)
{
if (!Resolve(uid, ref microphone))
return;
if (value == microphone.Enabled)
return;
microphone.Enabled = value;
if (value)
EnsureComp<ActiveListenerComponent>(uid).Range = microphone.Range;
else
RemCompDeferred<ActiveListenerComponent>(uid);
}
}
public sealed class SurveillanceCameraSpeechSendEvent : EntityEventArgs
{
public EntityUid Speaker { get; }
public string Message { get; }
public SurveillanceCameraSpeechSendEvent(EntityUid speaker, string message)
{
Speaker = speaker;
Message = message;
}
}