diff --git a/Content.Client/_White/DeepSpaceCom/DeepSpaceComBoundUI.cs b/Content.Client/_White/DeepSpaceCom/DeepSpaceComBoundUI.cs
new file mode 100644
index 0000000000..1f6d1a2555
--- /dev/null
+++ b/Content.Client/_White/DeepSpaceCom/DeepSpaceComBoundUI.cs
@@ -0,0 +1,57 @@
+using Content.Shared._White.DeepSpaceCom;
+using JetBrains.Annotations;
+
+namespace Content.Client._White.DeepSpaceCom;
+
+[UsedImplicitly]
+public sealed class DeepSpaceComBoundUI : BoundUserInterface
+{
+ [ViewVariables]
+ private DeepSpaceComMenu? _menu;
+
+ public DeepSpaceComBoundUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = new();
+
+ _menu.OnMicPressed += enabled =>
+ {
+ SendMessage(new ToggleDeepSpaceComMicrophoneMessage(enabled));
+ };
+ _menu.OnSpeakerPressed += enabled =>
+ {
+ SendMessage(new ToggleDeepSpaceComSpeakerMessage(enabled));
+ };
+ _menu.OnChannelSelected += channel =>
+ {
+ SendMessage(new SelectDeepSpaceComChannelMessage(channel));
+ };
+
+ _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 DeepSpaceComBoundUIState msg)
+ return;
+
+ _menu?.Update(msg);
+ }
+}
diff --git a/Content.Client/_White/DeepSpaceCom/DeepSpaceComMenu.xaml b/Content.Client/_White/DeepSpaceCom/DeepSpaceComMenu.xaml
new file mode 100644
index 0000000000..71bd6e3616
--- /dev/null
+++ b/Content.Client/_White/DeepSpaceCom/DeepSpaceComMenu.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/_White/DeepSpaceCom/DeepSpaceComMenu.xaml.cs b/Content.Client/_White/DeepSpaceCom/DeepSpaceComMenu.xaml.cs
new file mode 100644
index 0000000000..258f0c3f5f
--- /dev/null
+++ b/Content.Client/_White/DeepSpaceCom/DeepSpaceComMenu.xaml.cs
@@ -0,0 +1,59 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared._White.DeepSpaceCom;
+using Content.Shared.Radio;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client._White.DeepSpaceCom;
+
+[GenerateTypedNameReferences]
+public sealed partial class DeepSpaceComMenu : FancyWindow
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public event Action? OnMicPressed;
+ public event Action? OnSpeakerPressed;
+ public event Action? OnChannelSelected;
+
+ private readonly List _channels = new();
+
+ public DeepSpaceComMenu()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ MicButton.OnPressed += args => OnMicPressed?.Invoke(args.Button.Pressed);
+ SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed);
+ }
+
+ public void Update(DeepSpaceComBoundUIState state)
+ {
+ MicButton.Pressed = state.MicEnabled;
+ SpeakerButton.Pressed = state.SpeakerEnabled;
+
+ ChannelOptions.Clear();
+ _channels.Clear();
+ for (var i = 0; i < state.AvailableChannels.Count; i++)
+ {
+ var channel = state.AvailableChannels[i];
+ if (!_prototype.TryIndex(channel, out var prototype))
+ continue;
+
+ _channels.Add(channel);
+ ChannelOptions.AddItem(Loc.GetString(prototype.Name), i);
+
+ if (channel == state.SelectedChannel)
+ ChannelOptions.Select(i);
+ }
+ ChannelOptions.OnItemSelected += args =>
+ {
+ ChannelOptions.SelectId(args.Id);
+ OnChannelSelected?.Invoke(_channels[args.Id]);
+ MicButton.Pressed = false;
+ SpeakerButton.Pressed = false;
+ OnMicPressed?.Invoke(false);
+ OnSpeakerPressed?.Invoke(false);
+ };
+ }
+}
diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
index c26b4cae30..0e4192053c 100644
--- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
@@ -4,6 +4,7 @@ using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Radio.Components;
+using Content.Shared._White.DeepSpaceCom; // WD
using Content.Server.Speech;
using Content.Server.Speech.Components;
using Content.Shared.UserInterface;
@@ -51,6 +52,11 @@ public sealed class RadioDeviceSystem : EntitySystem
SubscribeLocalEvent(OnToggleIntercomMic);
SubscribeLocalEvent(OnToggleIntercomSpeaker);
SubscribeLocalEvent(OnSelectIntercomChannel);
+
+ SubscribeLocalEvent(OnBeforeDeepSpaceComUiOpen); // WD start
+ SubscribeLocalEvent(OnToggleDeepSpaceComMic);
+ SubscribeLocalEvent(OnToggleDeepSpaceComSpeaker);
+ SubscribeLocalEvent(OnSelectDeepSpaceComChannel); // WD end
}
public override void Update(float frameTime)
@@ -264,4 +270,55 @@ public sealed class RadioDeviceSystem : EntitySystem
var state = new IntercomBoundUIState(micEnabled, speakerEnabled, availableChannels, selectedChannel);
_ui.SetUiState(uid, IntercomUiKey.Key, state);
}
+
+ private void OnBeforeDeepSpaceComUiOpen(EntityUid uid, DeepSpaceComComponent component, BeforeActivatableUIOpenEvent args) // WD start
+ {
+ UpdateDeepSpaceComUi(uid, component);
+ }
+
+ private void OnToggleDeepSpaceComMic(EntityUid uid, DeepSpaceComComponent component, ToggleDeepSpaceComMicrophoneMessage args)
+ {
+ if (component.RequiresPower && !this.IsPowered(uid, EntityManager))
+ return;
+
+ SetMicrophoneEnabled(uid, args.Actor, args.Enabled, true);
+ UpdateDeepSpaceComUi(uid, component);
+ }
+
+ private void OnToggleDeepSpaceComSpeaker(EntityUid uid, DeepSpaceComComponent component, ToggleDeepSpaceComSpeakerMessage args)
+ {
+ if (component.RequiresPower && !this.IsPowered(uid, EntityManager))
+ return;
+
+ SetSpeakerEnabled(uid, args.Actor, args.Enabled, true);
+ UpdateDeepSpaceComUi(uid, component);
+ }
+
+ private void OnSelectDeepSpaceComChannel(EntityUid uid, DeepSpaceComComponent component, SelectDeepSpaceComChannelMessage args)
+ {
+ if (component.RequiresPower && !this.IsPowered(uid, EntityManager))
+ return;
+
+ if (!_protoMan.TryIndex(args.Channel, out _) || !component.SupportedChannels.Contains(args.Channel))
+ return;
+
+ if (TryComp(uid, out var mic))
+ mic.BroadcastChannel = args.Channel;
+ if (TryComp(uid, out var speaker))
+ speaker.Channels = new(){ args.Channel };
+ UpdateDeepSpaceComUi(uid, component);
+ }
+
+ private void UpdateDeepSpaceComUi(EntityUid uid, DeepSpaceComComponent component)
+ {
+ var micComp = CompOrNull(uid);
+ var speakerComp = CompOrNull(uid);
+
+ var micEnabled = micComp?.Enabled ?? false;
+ var speakerEnabled = speakerComp?.Enabled ?? false;
+ var availableChannels = component.SupportedChannels;
+ var selectedChannel = micComp?.BroadcastChannel ?? SharedChatSystem.CommonChannel;
+ var state = new DeepSpaceComBoundUIState(micEnabled, speakerEnabled, availableChannels, selectedChannel);
+ _ui.SetUiState(uid, DeepSpaceComUiKey.Key, state);
+ } // WD end
}
diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs
index 2fca1b2806..48d7938001 100644
--- a/Content.Server/Radio/EntitySystems/RadioSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs
@@ -3,6 +3,7 @@ using Content.Server.Chat.Systems;
using Content.Server.Power.Components;
using Content.Server.Radio.Components;
using Content.Server.VoiceMask;
+using Content.Shared._White.DeepSpaceCom; // WD
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Radio;
@@ -127,12 +128,13 @@ public sealed class RadioSystem : EntitySystem
var radioQuery = EntityQueryEnumerator();
while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform))
{
- if (!radio.ReceiveAllChannels)
+ if (!radio.ReceiveAllChannels) // WD start
{
- if (!radio.Channels.Contains(channel.ID) || (TryComp(receiver, out var intercom) &&
- !intercom.SupportedChannels.Contains(channel.ID)))
+ if (!radio.Channels.Contains(channel.ID) ||
+ (TryComp(receiver, out var deepSpaceCom) && !deepSpaceCom.SupportedChannels.Contains(channel.ID)) ||
+ (TryComp(receiver, out var intercom) && !intercom.SupportedChannels.Contains(channel.ID)))
continue;
- }
+ } // WD end
if (!channel.LongRange && transform.MapID != sourceMapId && !radio.GlobalReceive)
continue;
diff --git a/Content.Shared/_White/DeepSpaceCom/DeepSpaceComComponent.cs b/Content.Shared/_White/DeepSpaceCom/DeepSpaceComComponent.cs
new file mode 100644
index 0000000000..a7fbe1745b
--- /dev/null
+++ b/Content.Shared/_White/DeepSpaceCom/DeepSpaceComComponent.cs
@@ -0,0 +1,15 @@
+using Content.Shared.Radio;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._White.DeepSpaceCom;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class DeepSpaceComComponent : Component
+{
+ [DataField("requiresPower"), ViewVariables(VVAccess.ReadWrite)]
+ public bool RequiresPower = true;
+
+ [DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer))]
+ public List SupportedChannels = new();
+}
diff --git a/Content.Shared/_White/DeepSpaceCom/SharedDeepSpaceCom.cs b/Content.Shared/_White/DeepSpaceCom/SharedDeepSpaceCom.cs
new file mode 100644
index 0000000000..677a217278
--- /dev/null
+++ b/Content.Shared/_White/DeepSpaceCom/SharedDeepSpaceCom.cs
@@ -0,0 +1,59 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._White.DeepSpaceCom;
+
+[Serializable, NetSerializable]
+public enum DeepSpaceComUiKey
+{
+ Key
+}
+
+[Serializable, NetSerializable]
+public sealed class DeepSpaceComBoundUIState : BoundUserInterfaceState
+{
+ public bool MicEnabled;
+ public bool SpeakerEnabled;
+ public List AvailableChannels;
+ public string SelectedChannel;
+
+ public DeepSpaceComBoundUIState(bool micEnabled, bool speakerEnabled, List availableChannels, string selectedChannel)
+ {
+ MicEnabled = micEnabled;
+ SpeakerEnabled = speakerEnabled;
+ AvailableChannels = availableChannels;
+ SelectedChannel = selectedChannel;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ToggleDeepSpaceComMicrophoneMessage : BoundUserInterfaceMessage
+{
+ public bool Enabled;
+
+ public ToggleDeepSpaceComMicrophoneMessage(bool enabled)
+ {
+ Enabled = enabled;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ToggleDeepSpaceComSpeakerMessage : BoundUserInterfaceMessage
+{
+ public bool Enabled;
+
+ public ToggleDeepSpaceComSpeakerMessage(bool enabled)
+ {
+ Enabled = enabled;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class SelectDeepSpaceComChannelMessage : BoundUserInterfaceMessage
+{
+ public string Channel;
+
+ public SelectDeepSpaceComChannelMessage(string channel)
+ {
+ Channel = channel;
+ }
+}
diff --git a/Resources/Locale/ru-RU/_white/radio/deepspacecom.ftl b/Resources/Locale/ru-RU/_white/radio/deepspacecom.ftl
new file mode 100644
index 0000000000..51717b41c0
--- /dev/null
+++ b/Resources/Locale/ru-RU/_white/radio/deepspacecom.ftl
@@ -0,0 +1,7 @@
+ent-ComputerDeepSpaceCom = консоль дальней связи
+ .desc = Дальняя космическая связь обеспечивает быстрый обмен сообщениями почти на любом расстоянии. Корпорация слышит!
+
+ent-DeepSpaceComComputerCircuitboard = печатная плата пульта дальней связи
+ .desc = Печатная плата для пульта дальней космической связи.
+
+chat-radio-deepspace = Дальняя связь
diff --git a/Resources/Locale/ru-RU/_white/radio/deepspacecomui.ftl b/Resources/Locale/ru-RU/_white/radio/deepspacecomui.ftl
new file mode 100644
index 0000000000..505ac52005
--- /dev/null
+++ b/Resources/Locale/ru-RU/_white/radio/deepspacecomui.ftl
@@ -0,0 +1,5 @@
+deepspacecom-menu-title = Пульт дальней космической связи
+deepspacecom-channel-label = Частота:
+deepspacecom-button-text-mic = Микрофон
+deepspacecom-button-text-speaker = Динамик
+deepspacecom-flavor-text = Поиск сигналов...
diff --git a/Resources/Prototypes/_White/Structures/Machines/deepspacecom.yml b/Resources/Prototypes/_White/Structures/Machines/deepspacecom.yml
new file mode 100644
index 0000000000..51ab356e1e
--- /dev/null
+++ b/Resources/Prototypes/_White/Structures/Machines/deepspacecom.yml
@@ -0,0 +1,61 @@
+- type: entity
+ parent: BaseComputer
+ id: ComputerDeepSpaceCom
+ name: deep space communications desk
+ description: A computer.
+ components:
+ - type: ApcPowerReceiver
+ - type: Electrified
+ enabled: false
+ usesApcPower: true
+ - type: RadioMicrophone
+ powerRequired: true
+ unobstructedRequired: true
+ listenRange: 2
+ toggleOnInteract: false
+ - type: RadioSpeaker
+ toggleOnInteract: false
+ - type: DeepSpaceCom
+ supportedChannels:
+ - DeepSpace
+ - Common
+ - type: TTS # check tts work
+ id: Sentrybot
+ - type: Speech
+ speechVerb: Robotic
+ - type: Sprite # replace sprites in future
+ layers:
+ - map: ["computerLayerBody"]
+ state: computer
+ - map: ["computerLayerKeyboard"]
+ state: generic_keyboard
+ - map: ["computerLayerScreen"]
+ state: comm
+ - map: ["computerLayerKeys"]
+ state: generic_keys
+ - type: ActivatableUI
+ key: enum.DeepSpaceComUiKey.Key
+ - type: UserInterface
+ interfaces:
+ enum.DeepSpaceComUiKey.Key:
+ type: DeepSpaceComBoundUI
+ - type: Computer
+ board: DeepSpaceComComputerCircuitboard
+ - type: PointLight
+ radius: 1.5
+ energy: 1.6
+ color: "#3c5eb5"
+ - type: Damageable
+ damageContainer: StructuralInorganic
+ damageModifierSet: StrongMetallic
+
+- type: entity
+ parent: BaseComputerCircuitboard
+ id: DeepSpaceComComputerCircuitboard
+ name: deepspacecom computer board
+ description: A computer printed circuit board for a DeepSpaceCom desk.
+ components:
+ - type: Sprite
+ state: cpu_command
+ - type: ComputerBoard
+ prototype: ComputerDeepSpaceCom
diff --git a/Resources/Prototypes/radio_channels.yml b/Resources/Prototypes/radio_channels.yml
index 57dbb3feb8..dea460baa5 100644
--- a/Resources/Prototypes/radio_channels.yml
+++ b/Resources/Prototypes/radio_channels.yml
@@ -118,3 +118,9 @@
color: "#f6ce64"
# long range since otherwise it'd defeat the point of a handheld radio independent of telecomms
longRange: true
+
+- type: radioChannel # WD start
+ id: DeepSpace
+ name: chat-radio-deepspace
+ frequency: 1501
+ longRange: true # WD end