diff --git a/Content.Client/Radio/Ui/HandheldRadioBoundUserInterface.cs b/Content.Client/Radio/Ui/HandheldRadioBoundUserInterface.cs
new file mode 100644
index 00000000000..c16d1649026
--- /dev/null
+++ b/Content.Client/Radio/Ui/HandheldRadioBoundUserInterface.cs
@@ -0,0 +1,59 @@
+using Content.Shared.Radio;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Radio.Ui;
+
+
+[UsedImplicitly]
+public sealed class HandheldRadioBoundUserInterface : BoundUserInterface
+{
+ [ViewVariables]
+ private HandheldRadioMenu? _menu;
+
+ public HandheldRadioBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = new();
+
+ _menu.OnMicPressed += enabled =>
+ {
+ SendMessage(new ToggleHandheldRadioMicMessage(enabled));
+ };
+ _menu.OnSpeakerPressed += enabled =>
+ {
+ SendMessage(new ToggleHandheldRadioSpeakerMessage(enabled));
+ };
+ _menu.OnFrequencyChanged += frequency =>
+ {
+ SendMessage(new SelectHandheldRadioFrequencyMessage(frequency));
+ };
+
+ _menu.OnClose += Close;
+ _menu.OpenCentered();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+ _menu?.Close();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (state is not HandheldRadioBoundUIState msg)
+ return;
+
+ _menu?.Update(msg);
+ }
+}
diff --git a/Content.Client/Radio/Ui/HandheldRadioMenu.xaml b/Content.Client/Radio/Ui/HandheldRadioMenu.xaml
new file mode 100644
index 00000000000..3877d1ead23
--- /dev/null
+++ b/Content.Client/Radio/Ui/HandheldRadioMenu.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Radio/Ui/HandheldRadioMenu.xaml.cs b/Content.Client/Radio/Ui/HandheldRadioMenu.xaml.cs
new file mode 100644
index 00000000000..08f99d55c04
--- /dev/null
+++ b/Content.Client/Radio/Ui/HandheldRadioMenu.xaml.cs
@@ -0,0 +1,36 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Radio;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Radio.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class HandheldRadioMenu : FancyWindow
+{
+
+ public event Action? OnMicPressed;
+ public event Action? OnSpeakerPressed;
+ public event Action? OnFrequencyChanged;
+
+ public HandheldRadioMenu()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ MicButton.OnPressed += args => OnMicPressed?.Invoke(args.Button.Pressed);
+ SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed);
+
+ FrequencyLineEdit.OnTextEntered += e => OnFrequencyChanged?.Invoke(e.Text);
+ FrequencyLineEdit.OnFocusExit += e => OnFrequencyChanged?.Invoke(e.Text);
+ }
+
+ public void Update(HandheldRadioBoundUIState state)
+ {
+ MicButton.Pressed = state.MicEnabled;
+ SpeakerButton.Pressed = state.SpeakerEnabled;
+ FrequencyLineEdit.Text = state.Frequency.ToString();
+ }
+}
+
diff --git a/Content.Server/Radio/Components/RadioMicrophoneComponent.cs b/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
index af01f86f23c..cec39626aa1 100644
--- a/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
+++ b/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
@@ -34,6 +34,13 @@ public sealed partial class RadioMicrophoneComponent : Component
[DataField("toggleOnInteract")]
public bool ToggleOnInteract = true;
+ // Corvax-Frontier
+ ///
+ // The radio frequency on which the message will be transmitted
+ ///
+ [DataField]
+ public int Frequency = 1459; // Common channel frequency
+
///
/// Whether or not the speaker must have an
/// unobstructed path to the radio to speak
diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
index ace7d8ae31a..d156847aeea 100644
--- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
@@ -51,6 +51,13 @@ public override void Initialize()
SubscribeLocalEvent(OnToggleIntercomMic);
SubscribeLocalEvent(OnToggleIntercomSpeaker);
SubscribeLocalEvent(OnSelectIntercomChannel);
+
+ // Corvax-Frontier-Start
+ SubscribeLocalEvent(OnBeforeHandheldRadioUiOpen);
+ SubscribeLocalEvent(OnToggleHandheldRadioMic);
+ SubscribeLocalEvent(OnToggleHandheldRadioSpeaker);
+ SubscribeLocalEvent(OnChangeHandheldRadioFrequency);
+ // Corvax-Frontier-End
}
public override void Update(float frameTime)
@@ -175,7 +182,7 @@ private void OnExamine(EntityUid uid, RadioMicrophoneComponent component, Examin
using (args.PushGroup(nameof(RadioMicrophoneComponent)))
{
- args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency)));
+ args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", component.Frequency)));
args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine",
("channel", proto.LocalizedName)));
}
@@ -187,7 +194,7 @@ private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenE
return; // no feedback loops please.
if (_recentlySent.Add((args.Message, args.Source)))
- _radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index(component.BroadcastChannel), uid);
+ _radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index(component.BroadcastChannel), uid, frequency: component.Frequency);
}
private void OnAttemptListen(EntityUid uid, RadioMicrophoneComponent component, ListenAttemptEvent args)
@@ -239,11 +246,14 @@ private void OnSelectIntercomChannel(EntityUid uid, IntercomComponent component,
if (component.RequiresPower && !this.IsPowered(uid, EntityManager) || args.Session.AttachedEntity is not { })
return;
- if (!_protoMan.TryIndex(args.Channel, out _) || !component.SupportedChannels.Contains(args.Channel))
+ if (!_protoMan.TryIndex(args.Channel, out var channel) || !component.SupportedChannels.Contains(args.Channel))
return;
if (TryComp(uid, out var mic))
+ {
mic.BroadcastChannel = args.Channel;
+ mic.Frequency = _radio.GetFrequency(uid, channel); // Corvax-Frontier
+ }
if (TryComp(uid, out var speaker))
speaker.Channels = new(){ args.Channel };
UpdateIntercomUi(uid, component);
@@ -261,4 +271,53 @@ private void UpdateIntercomUi(EntityUid uid, IntercomComponent component)
var state = new IntercomBoundUIState(micEnabled, speakerEnabled, availableChannels, selectedChannel);
_ui.TrySetUiState(uid, IntercomUiKey.Key, state);
}
+
+ // Corvax-Frontier-Start
+ #region Handheld Radio
+
+ private void OnBeforeHandheldRadioUiOpen(Entity microphone, ref BeforeActivatableUIOpenEvent args)
+ {
+ UpdateHandheldRadioUi(microphone);
+ }
+
+ private void OnToggleHandheldRadioMic(Entity microphone, ref ToggleHandheldRadioMicMessage args)
+ {
+ if (args.Session.AttachedEntity is not { } user)
+ return;
+
+ SetMicrophoneEnabled(microphone, user, args.Enabled, true);
+ UpdateHandheldRadioUi(microphone);
+ }
+
+ private void OnToggleHandheldRadioSpeaker(Entity microphone, ref ToggleHandheldRadioSpeakerMessage args)
+ {
+ if (args.Session.AttachedEntity is not { } user)
+ return;
+
+ SetSpeakerEnabled(microphone, user, args.Enabled, true);
+ UpdateHandheldRadioUi(microphone);
+ }
+
+ private void OnChangeHandheldRadioFrequency(Entity microphone, ref SelectHandheldRadioFrequencyMessage args)
+ {
+ if (args.Session.AttachedEntity is not { })
+ return;
+
+ microphone.Comp.Frequency = args.Frequency;
+ UpdateHandheldRadioUi(microphone);
+ }
+
+ private void UpdateHandheldRadioUi(Entity radio)
+ {
+ var speakerComp = CompOrNull(radio);
+ var frequency = radio.Comp.Frequency;
+
+ var micEnabled = radio.Comp.Enabled;
+ var speakerEnabled = speakerComp?.Enabled ?? false;
+ var state = new HandheldRadioBoundUIState(micEnabled, speakerEnabled, frequency);
+ _ui.TrySetUiState(radio, HandheldRadioUiKey.Key, state);
+ }
+
+ #endregion
+ // Corvax-Frontier-End
}
diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs
index 7b54d7daff7..a0734157e3a 100644
--- a/Content.Server/Radio/EntitySystems/RadioSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs
@@ -9,6 +9,7 @@
using Content.Shared.Radio.Components;
using Robust.Server.GameObjects;
using Content.Shared.Speech;
+using Content.Shared.Ghost; // Corvax-Frontier
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
@@ -65,12 +66,23 @@ public void SendRadioMessage(EntityUid messageSource, string message, ProtoId
+ /// Gets the message frequency, if there is no such frequency, returns the standard channel frequency.
+ ///
+ public int GetFrequency(EntityUid source, RadioChannelPrototype channel)
+ {
+ if (TryComp(source, out var radioMicrophone))
+ return radioMicrophone.Frequency;
+
+ return channel.Frequency;
+ } // Corvax-Frontier
+
///
/// Send radio message to all active radio listeners
///
/// Entity that spoke the message
/// Entity that picked up the message and will send it, e.g. headset
- public void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, bool escapeMarkup = true)
+ public void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, int? frequency = null, bool escapeMarkup = true)
{
// TODO if radios ever garble / modify messages, feedback-prevention needs to be handled better than this.
if (!_messages.Add(message))
@@ -144,6 +156,10 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann
var speakerQuery = GetEntityQuery();
var radioQuery = EntityQueryEnumerator();
+
+ if (frequency == null)
+ frequency = GetFrequency(messageSource, channel);
+
while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform))
{
if (!radio.ReceiveAllChannels)
@@ -152,6 +168,9 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann
!intercom.SupportedChannels.Contains(channel.ID)))
continue;
}
+ // Corvax-Frontier
+ if (!HasComp(receiver) && GetFrequency(receiver, channel) != frequency)
+ continue;
if (!channel.LongRange && transform.MapID != sourceMapId && !radio.GlobalReceive)
continue;
diff --git a/Content.Shared/Radio/SharedHandheldRadio.cs b/Content.Shared/Radio/SharedHandheldRadio.cs
new file mode 100644
index 00000000000..d6166b1c112
--- /dev/null
+++ b/Content.Shared/Radio/SharedHandheldRadio.cs
@@ -0,0 +1,57 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Radio;
+
+[Serializable, NetSerializable]
+public enum HandheldRadioUiKey : byte
+{
+ Key,
+}
+
+[Serializable, NetSerializable]
+public sealed class HandheldRadioBoundUIState : BoundUserInterfaceState
+{
+ public bool MicEnabled;
+ public bool SpeakerEnabled;
+ public int Frequency;
+
+ public HandheldRadioBoundUIState(bool micEnabled, bool speakerEnabled, int frequency)
+ {
+ MicEnabled = micEnabled;
+ SpeakerEnabled = speakerEnabled;
+ Frequency = frequency;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ToggleHandheldRadioMicMessage : BoundUserInterfaceMessage
+{
+ public bool Enabled;
+
+ public ToggleHandheldRadioMicMessage(bool enabled)
+ {
+ Enabled = enabled;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ToggleHandheldRadioSpeakerMessage : BoundUserInterfaceMessage
+{
+ public bool Enabled;
+
+ public ToggleHandheldRadioSpeakerMessage(bool enabled)
+ {
+ Enabled = enabled;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class SelectHandheldRadioFrequencyMessage : BoundUserInterfaceMessage
+{
+ public int Frequency;
+
+ public SelectHandheldRadioFrequencyMessage(string frequency)
+ {
+ int.TryParse(frequency.Trim(), out Frequency);
+ }
+}
diff --git a/Resources/Locale/en-US/ss14-ru/radio/components/handheld.ftl b/Resources/Locale/en-US/ss14-ru/radio/components/handheld.ftl
new file mode 100644
index 00000000000..d59c356561b
--- /dev/null
+++ b/Resources/Locale/en-US/ss14-ru/radio/components/handheld.ftl
@@ -0,0 +1,6 @@
+handheld-radio-menu-title = Handheld radio.
+handheld-radio-current-text-frequency = Broadcast frequency
+handheld-radio-button-text-mic = Mic.
+handheld-radio-button-text-speaker = Speak
+handheld-radio-flavor-text-left = Wiretapping of closed frequencies
+ is punishable by law.
diff --git a/Resources/Locale/ru-RU/radio/components/handheld.ftl b/Resources/Locale/ru-RU/radio/components/handheld.ftl
new file mode 100644
index 00000000000..51f5577235b
--- /dev/null
+++ b/Resources/Locale/ru-RU/radio/components/handheld.ftl
@@ -0,0 +1,6 @@
+handheld-radio-menu-title = Портативная рация.
+handheld-radio-current-text-frequency = Частота вещания
+handheld-radio-button-text-mic = Микр.
+handheld-radio-button-text-speaker = Динам.
+handheld-radio-flavor-text-left = Прослушивание закрытых частот
+ карается законом.
diff --git a/Resources/Prototypes/Entities/Objects/Devices/radio.yml b/Resources/Prototypes/Entities/Objects/Devices/radio.yml
index ebf87c772cc..6210907d723 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/radio.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/radio.yml
@@ -6,9 +6,12 @@
components:
- type: RadioMicrophone
broadcastChannel: Handheld
+ toggleOnInteract: false
+ frequency: 1330
- type: RadioSpeaker
channels:
- Handheld
+ toggleOnInteract: false
- type: Speech
speechVerb: Robotic
- type: Sprite
@@ -20,6 +23,12 @@
- type: Item
sprite: Objects/Devices/communication.rsi
heldPrefix: walkietalkie
+ - type: ActivatableUI
+ key: enum.HandheldRadioUiKey.Key
+ - type: UserInterface
+ interfaces:
+ - key: enum.HandheldRadioUiKey.Key
+ type: HandheldRadioBoundUserInterface
- type: Tag
tags:
- Radio