using Content.Shared.Cuffs; // DeltaV using Content.Shared.Cuffs.Components; // DeltaV using Content.Shared.Hands.Components; // DeltaV using Content.Shared.Chat; using Content.Shared.Inventory.Events; using Content.Shared.Popups; // DeltaV using Content.Shared.Radio; using Content.Shared.Radio.Components; using Content.Shared.Radio.EntitySystems; using Robust.Shared.Network; using Robust.Shared.Player; using Content.Shared._DV.Chat; namespace Content.Server.Radio.EntitySystems; public sealed class HeadsetSystem : SharedHeadsetSystem { [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly RadioSystem _radio = default!; [Dependency] private readonly SharedCuffableSystem _cuffable = default!; // DeltaV [Dependency] private readonly SharedPopupSystem _popup = default!; // DeltaV public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnHeadsetReceive); SubscribeLocalEvent(OnKeysChanged); SubscribeLocalEvent(OnSpeak); SubscribeLocalEvent(OnAudibleEmote); // DeltaV } private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args) { UpdateRadioChannels(uid, component, args.Component); } private void UpdateRadioChannels(EntityUid uid, HeadsetComponent headset, EncryptionKeyHolderComponent? keyHolder = null) { // make sure to not add ActiveRadioComponent when headset is being deleted if (!headset.Enabled || MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating) return; if (!Resolve(uid, ref keyHolder)) return; if (keyHolder.Channels.Count == 0) RemComp(uid); else EnsureComp(uid).Channels = new(keyHolder.Channels); } // DeltaV // WARNING - Be very careful when modifying this method. // The implementation right now has null forgiving operatiors in OnSpeak() and OnAudibleEmote() since this only returns true if channel is not null private bool CheckRadioCapable(EntityUid uid, WearingHeadsetComponent headset, RadioChannelPrototype? channel) { if (channel != null && TryComp(headset.Headset, out EncryptionKeyHolderComponent? keys) && keys.Channels.Contains(channel.ID)) { if (!TryComp(uid, out var hands) || hands.Count < 1 || TryComp(uid, out var cuffable) && _cuffable.IsCuffed((uid, cuffable))) { _popup.PopupEntity(Loc.GetString("headset-cant-reach"), uid, uid, PopupType.SmallCaution); return false; } return true; } return false; } private void OnAudibleEmote(EntityUid uid, WearingHeadsetComponent component, EntityAudiblyEmotedEvent args) { if (CheckRadioCapable(uid, component, args.Channel)) { _radio.SendRadioMessage(uid, args.Message, args.Channel!, component.Headset, emType: args.Type); args.Channel = null; } } // DeltaV - End private void OnSpeak(EntityUid uid, WearingHeadsetComponent component, EntitySpokeEvent args) { if (CheckRadioCapable(uid, component, args.Channel)) // DeltaV - Put all the radio checks into the CheckRadioCapable() method. { _radio.SendRadioMessage(uid, args.Message, args.Channel!, component.Headset); // DeltaV - Made the args.Channel null ignorant as CheckRadioCapable() guarantees it not null. args.Channel = null; // prevent duplicate messages from other listeners. } } protected override void OnGotEquipped(EntityUid uid, HeadsetComponent component, GotEquippedEvent args) { base.OnGotEquipped(uid, component, args); if (component.IsEquipped && component.Enabled) { EnsureComp(args.Equipee).Headset = uid; UpdateRadioChannels(uid, component); } } protected override void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args) { base.OnGotUnequipped(uid, component, args); RemComp(uid); RemComp(args.Equipee); } public void SetEnabled(EntityUid uid, bool value, HeadsetComponent? component = null) { if (!Resolve(uid, ref component)) return; if (component.Enabled == value) return; component.Enabled = value; Dirty(uid, component); if (!value) { RemCompDeferred(uid); if (component.IsEquipped) RemCompDeferred(Transform(uid).ParentUid); } else if (component.IsEquipped) { EnsureComp(Transform(uid).ParentUid).Headset = uid; UpdateRadioChannels(uid, component); } } private void OnHeadsetReceive(EntityUid uid, HeadsetComponent component, ref RadioReceiveEvent args) { // TODO: change this when a code refactor is done // this is currently done this way because receiving radio messages on an entity otherwise requires that entity // to have an ActiveRadioComponent var parent = Transform(uid).ParentUid; if (parent.IsValid()) { var relayEvent = new HeadsetRadioReceiveRelayEvent(args); RaiseLocalEvent(parent, ref relayEvent); } if (TryComp(parent, out ActorComponent? actor)) _netMan.ServerSendMessage(args.ChatMsg, actor.PlayerSession.Channel); } }