Files
wwdpublic/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
dootythefrooty e3b216bdde Intercomms Fix (#2269)
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Description.

---
Intercomms were turbo broken because the frequency didn't change from
the default "common" frequency causing it to be only useful for common.
Now they work for any channel as they were supposed to.

<details><summary><h1>Media</h1></summary>
<p>

https://github.com/user-attachments/assets/3abb1e17-9f1b-4ba4-b165-ba94de82501f

</p>
</details>

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑
- fix: Intercomms are functional for every channel again.

(cherry picked from commit da70c3ba29003756bd994c18dfaaf554aa7027c1)
2025-04-19 17:07:42 +03:00

384 lines
15 KiB
C#

using System.Linq;
using Content.Server._White.Hearing;
using Content.Server.Chat.Systems;
using Content.Server.Interaction;
using Content.Server.Language;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Radio.Components;
using Content.Server.Speech;
using Content.Server.Speech.Components;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Power;
using Content.Shared.Radio;
using Content.Shared.Chat;
using Content.Shared.Radio.Components;
using Content.Shared.UserInterface; // Nuclear-14
using Content.Shared._NC.Radio; // Nuclear-14
using Robust.Server.GameObjects;
using Robust.Shared.Network;
using Robust.Shared.Player; // Nuclear-14
using Robust.Shared.Prototypes;
namespace Content.Server.Radio.EntitySystems;
/// <summary>
/// This system handles radio speakers and microphones (which together form a hand-held radio).
/// </summary>
public sealed class RadioDeviceSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly RadioSystem _radio = default!;
[Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly LanguageSystem _language = default!;
[Dependency] private readonly HearingSystem _hearing = default!;
// Used to prevent a shitter from using a bunch of radios to spam chat.
private HashSet<(string, EntityUid)> _recentlySent = new();
// Frontier: minimum, maximum radio frequencies
private const int MinRadioFrequency = 1000;
private const int MaxRadioFrequency = 3000;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RadioMicrophoneComponent, ComponentInit>(OnMicrophoneInit);
SubscribeLocalEvent<RadioMicrophoneComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<RadioMicrophoneComponent, ActivateInWorldEvent>(OnActivateMicrophone);
SubscribeLocalEvent<RadioMicrophoneComponent, ListenEvent>(OnListen);
SubscribeLocalEvent<RadioMicrophoneComponent, ListenAttemptEvent>(OnAttemptListen);
SubscribeLocalEvent<RadioMicrophoneComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<RadioSpeakerComponent, ComponentInit>(OnSpeakerInit);
SubscribeLocalEvent<RadioSpeakerComponent, ActivateInWorldEvent>(OnActivateSpeaker);
SubscribeLocalEvent<RadioSpeakerComponent, RadioReceiveEvent>(OnReceiveRadio);
SubscribeLocalEvent<IntercomComponent, EncryptionChannelsChangedEvent>(OnIntercomEncryptionChannelsChanged);
SubscribeLocalEvent<IntercomComponent, ToggleIntercomMicMessage>(OnToggleIntercomMic);
SubscribeLocalEvent<IntercomComponent, ToggleIntercomSpeakerMessage>(OnToggleIntercomSpeaker);
SubscribeLocalEvent<IntercomComponent, SelectIntercomChannelMessage>(OnSelectIntercomChannel);
SubscribeLocalEvent<RadioMicrophoneComponent, BeforeActivatableUIOpenEvent>(OnBeforeHandheldRadioUiOpen);
SubscribeLocalEvent<RadioMicrophoneComponent, ToggleHandheldRadioMicMessage>(OnToggleHandheldRadioMic);
SubscribeLocalEvent<RadioMicrophoneComponent, ToggleHandheldRadioSpeakerMessage>(OnToggleHandheldRadioSpeaker);
SubscribeLocalEvent<RadioMicrophoneComponent, SelectHandheldRadioFrequencyMessage>(OnChangeHandheldRadioFrequency);
SubscribeLocalEvent<IntercomComponent, MapInitEvent>(OnMapInit); // Frontier
}
public override void Update(float frameTime)
{
base.Update(frameTime);
_recentlySent.Clear();
}
#region Component Init
private void OnMicrophoneInit(EntityUid uid, RadioMicrophoneComponent component, ComponentInit args)
{
if (component.Enabled)
EnsureComp<ActiveListenerComponent>(uid).Range = component.ListenRange;
else
RemCompDeferred<ActiveListenerComponent>(uid);
}
private void OnSpeakerInit(EntityUid uid, RadioSpeakerComponent component, ComponentInit args)
{
if (component.Enabled)
EnsureComp<ActiveRadioComponent>(uid).Channels.UnionWith(component.Channels);
else
RemCompDeferred<ActiveRadioComponent>(uid);
}
#endregion
#region Toggling
private void OnActivateMicrophone(EntityUid uid, RadioMicrophoneComponent component, ActivateInWorldEvent args)
{
if (!args.Complex)
return;
if (!component.ToggleOnInteract)
return;
ToggleRadioMicrophone(uid, args.User, args.Handled, component);
args.Handled = true;
}
private void OnActivateSpeaker(EntityUid uid, RadioSpeakerComponent component, ActivateInWorldEvent args)
{
if (!args.Complex)
return;
if (!component.ToggleOnInteract)
return;
ToggleRadioSpeaker(uid, args.User, args.Handled, component);
args.Handled = true;
}
public void ToggleRadioMicrophone(EntityUid uid, EntityUid user, bool quiet = false, RadioMicrophoneComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
SetMicrophoneEnabled(uid, user, !component.Enabled, quiet, component);
}
private void OnPowerChanged(EntityUid uid, RadioMicrophoneComponent component, ref PowerChangedEvent args)
{
if (args.Powered)
return;
SetMicrophoneEnabled(uid, null, false, true, component);
}
public void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null)
{
if (!Resolve(uid, ref component, false))
return;
if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
return;
component.Enabled = enabled;
if (!quiet && user != null)
{
var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state");
var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
_popup.PopupEntity(message, user.Value, user.Value);
}
_appearance.SetData(uid, RadioDeviceVisuals.Broadcasting, component.Enabled);
if (component.Enabled)
EnsureComp<ActiveListenerComponent>(uid).Range = component.ListenRange;
else
RemCompDeferred<ActiveListenerComponent>(uid);
}
public void ToggleRadioSpeaker(EntityUid uid, EntityUid user, bool quiet = false, RadioSpeakerComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
SetSpeakerEnabled(uid, user, !component.Enabled, quiet, component);
}
public void SetSpeakerEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioSpeakerComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
component.Enabled = enabled;
if (!quiet && user != null)
{
var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state");
var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
_popup.PopupEntity(message, user.Value, user.Value);
}
_appearance.SetData(uid, RadioDeviceVisuals.Speaker, component.Enabled);
if (component.Enabled)
EnsureComp<ActiveRadioComponent>(uid).Channels.UnionWith(component.Channels);
else
RemCompDeferred<ActiveRadioComponent>(uid);
}
#endregion
private void OnExamine(EntityUid uid, RadioMicrophoneComponent component, ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
var proto = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel);
using (args.PushGroup(nameof(RadioMicrophoneComponent)))
{
args.PushMarkup(Loc.GetString(
"handheld-radio-component-on-examine",
("frequency", component.Frequency),
("color", proto.Color.ToHex())));
args.PushMarkup(Loc.GetString(
"handheld-radio-component-channel-examine",
("channel", proto.LocalizedName),
("color", proto.Color.ToHex())));
}
}
private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenEvent args)
{
if (HasComp<RadioSpeakerComponent>(args.Source))
return; // no feedback loops please.
if (_recentlySent.Add((args.Message, args.Source)))
_radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel), uid, /*Nuclear-14-start*/ frequency: component.Frequency /*Nuclear-14-end*/);
}
private void OnAttemptListen(EntityUid uid, RadioMicrophoneComponent component, ListenAttemptEvent args)
{
if (component.PowerRequired && !this.IsPowered(uid, EntityManager)
|| component.UnobstructedRequired && !_interaction.InRangeUnobstructed(args.Source, uid, 0))
args.Cancel();
}
private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, ref RadioReceiveEvent args)
{
var parent = Transform(uid).ParentUid;
if (TryComp(parent, out ActorComponent? actor))
{
// WWDP Deafening
if (_hearing.IsBlockedByDeafness(actor.PlayerSession, ChatChannel.Radio, args.Language))
return;
// WWDP end
var canUnderstand = _language.CanUnderstand(parent, args.Language.ID);
var msg = new MsgChatMessage
{
Message = canUnderstand ? args.OriginalChatMsg : args.LanguageObfuscatedChatMsg
};
_netMan.ServerSendMessage(msg, actor.PlayerSession.Channel);
}
}
private void OnIntercomEncryptionChannelsChanged(Entity<IntercomComponent> ent, ref EncryptionChannelsChangedEvent args)
{
ent.Comp.SupportedChannels = args.Component.Channels.Select(p => new ProtoId<RadioChannelPrototype>(p)).ToList();
var channel = args.Component.DefaultChannel;
if (ent.Comp.CurrentChannel != null && ent.Comp.SupportedChannels.Contains(ent.Comp.CurrentChannel.Value))
channel = ent.Comp.CurrentChannel;
SetIntercomChannel(ent, channel);
}
private void OnToggleIntercomMic(Entity<IntercomComponent> ent, ref ToggleIntercomMicMessage args)
{
if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
return;
SetMicrophoneEnabled(ent, args.Actor, args.Enabled, true);
ent.Comp.MicrophoneEnabled = args.Enabled;
Dirty(ent);
}
private void OnToggleIntercomSpeaker(Entity<IntercomComponent> ent, ref ToggleIntercomSpeakerMessage args)
{
if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
return;
SetSpeakerEnabled(ent, args.Actor, args.Enabled, true);
ent.Comp.SpeakerEnabled = args.Enabled;
Dirty(ent);
}
private void OnSelectIntercomChannel(Entity<IntercomComponent> ent, ref SelectIntercomChannelMessage args)
{
if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
return;
if (!_protoMan.TryIndex<RadioChannelPrototype>(args.Channel, out var channel) || !ent.Comp.SupportedChannels.Contains(args.Channel)) // Nuclear-14: add channel
return;
SetIntercomChannel(ent, args.Channel);
}
private void SetIntercomChannel(Entity<IntercomComponent> ent, ProtoId<RadioChannelPrototype>? channel)
{
ent.Comp.CurrentChannel = channel;
if (channel == null)
{
SetSpeakerEnabled(ent, null, false);
SetMicrophoneEnabled(ent, null, false);
ent.Comp.MicrophoneEnabled = false;
ent.Comp.SpeakerEnabled = false;
Dirty(ent);
return;
}
if (TryComp<RadioMicrophoneComponent>(ent, out var mic))
{
mic.BroadcastChannel = channel;
if(_protoMan.TryIndex<RadioChannelPrototype>(channel, out var channelProto)) // Frontier
mic.Frequency = channelProto.Frequency; // Frontier
}
if (TryComp<RadioSpeakerComponent>(ent, out var speaker))
speaker.Channels = new(){ channel };
Dirty(ent);
}
// Nuclear-14-Start
#region Handheld Radio
private void OnBeforeHandheldRadioUiOpen(Entity<RadioMicrophoneComponent> microphone, ref BeforeActivatableUIOpenEvent args)
{
UpdateHandheldRadioUi(microphone);
}
private void OnToggleHandheldRadioMic(Entity<RadioMicrophoneComponent> microphone, ref ToggleHandheldRadioMicMessage args)
{
if (!args.Actor.Valid)
return;
SetMicrophoneEnabled(microphone, args.Actor, args.Enabled, true);
UpdateHandheldRadioUi(microphone);
}
private void OnToggleHandheldRadioSpeaker(Entity<RadioMicrophoneComponent> microphone, ref ToggleHandheldRadioSpeakerMessage args)
{
if (!args.Actor.Valid)
return;
SetSpeakerEnabled(microphone, args.Actor, args.Enabled, true);
UpdateHandheldRadioUi(microphone);
}
private void OnChangeHandheldRadioFrequency(Entity<RadioMicrophoneComponent> microphone, ref SelectHandheldRadioFrequencyMessage args)
{
if (!args.Actor.Valid)
return;
// Update frequency if valid and within range.
if (args.Frequency >= MinRadioFrequency && args.Frequency <= MaxRadioFrequency)
microphone.Comp.Frequency = args.Frequency;
// Update UI with current frequency.
UpdateHandheldRadioUi(microphone);
}
private void UpdateHandheldRadioUi(Entity<RadioMicrophoneComponent> radio)
{
var speakerComp = CompOrNull<RadioSpeakerComponent>(radio);
var frequency = radio.Comp.Frequency;
var micEnabled = radio.Comp.Enabled;
var speakerEnabled = speakerComp?.Enabled ?? false;
var state = new HandheldRadioBoundUIState(micEnabled, speakerEnabled, frequency);
if (TryComp<UserInterfaceComponent>(radio, out var uiComp))
_ui.SetUiState((radio.Owner, uiComp), HandheldRadioUiKey.Key, state); // Frontier: TrySetUiState<SetUiState
}
#endregion
// Nuclear-14-End
// Frontier: init intercom with map
private void OnMapInit(EntityUid uid, IntercomComponent ent, MapInitEvent args)
{
// Set initial frequency (must be done regardless of power/enabled)
if (ent.CurrentChannel != null &&
_protoMan.TryIndex(ent.CurrentChannel, out var channel) &&
TryComp(uid, out RadioMicrophoneComponent? mic))
{
mic.Frequency = channel.Frequency;
}
}
// End Frontier
}