]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
move all the radio components and system to Shared (#40293)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Thu, 11 Sep 2025 23:26:47 +0000 (01:26 +0200)
committerGitHub <noreply@github.com>
Thu, 11 Sep 2025 23:26:47 +0000 (01:26 +0200)
* move all the radio components and system to Shared.

* duh split impl

* address reviews

* cleanup

---------

Co-authored-by: walksanatora <walkerffo22@gmail.com>
24 files changed:
Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs
Content.Client/Silicons/Laws/Ui/LawDisplay.xaml.cs
Content.Server/Implants/RadioImplantSystem.cs
Content.Server/Radio/Components/IntrinsicRadioTransmitterComponent.cs [deleted file]
Content.Server/Radio/Components/RadioMicrophoneComponent.cs [deleted file]
Content.Server/Radio/Components/RadioSpeakerComponent.cs [deleted file]
Content.Server/Radio/EntitySystems/HeadsetSystem.cs
Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
Content.Server/Radio/EntitySystems/RadioSystem.cs
Content.Server/Silicons/Borgs/BorgSwitchableTypeSystem.cs
Content.Server/Silicons/Laws/SiliconLawSystem.cs
Content.Server/Speech/Components/ListenWireAction.cs
Content.Server/Vocalization/Systems/RadioVocalizationSystem.cs
Content.Shared/Radio/Components/ActiveRadioComponent.cs [moved from Content.Server/Radio/Components/ActiveRadioComponent.cs with 57% similarity]
Content.Shared/Radio/Components/EncryptionKeyComponent.cs
Content.Shared/Radio/Components/EncryptionKeyHolderComponent.cs
Content.Shared/Radio/Components/IntrinsicRadioReceiverComponent.cs [moved from Content.Server/Radio/Components/IntrinsicRadioReceiverComponent.cs with 73% similarity]
Content.Shared/Radio/Components/IntrinsicRadioTransmitterComponent.cs [new file with mode: 0644]
Content.Shared/Radio/Components/RadioMicrophoneComponent.cs [new file with mode: 0644]
Content.Shared/Radio/Components/RadioSpeakerComponent.cs [new file with mode: 0644]
Content.Shared/Radio/Components/WearingHeadsetComponent.cs [moved from Content.Server/Radio/Components/WearingHeadsetComponent.cs with 62% similarity]
Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
Content.Shared/Radio/EntitySystems/SharedRadioDeviceSystem.cs [new file with mode: 0644]
Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs

index 29d6c635ebc8df6d687884f7523bcf8cc063d59e..a2711a8257ca81e050def6f21c69c1b8acbd6ac8 100644 (file)
@@ -1,11 +1,12 @@
 using Content.Client.Radio.Ui;
 using Content.Shared.Radio;
 using Content.Shared.Radio.Components;
+using Content.Shared.Radio.EntitySystems;
 using Robust.Client.GameObjects;
 
 namespace Content.Client.Radio.EntitySystems;
 
-public sealed class RadioDeviceSystem : EntitySystem
+public sealed class RadioDeviceSystem : SharedRadioDeviceSystem
 {
     [Dependency] private readonly UserInterfaceSystem _ui = default!;
 
index 245ea194f04dddcd7bd0ec38f591876cfa62f378..55fb99a526d0867144f0d8ff2203a42d9111b328 100644 (file)
@@ -26,7 +26,7 @@ public sealed partial class LawDisplay : Control
 
     private readonly Dictionary<Button, TimeSpan> _nextAllowedPress = new();
 
-    public LawDisplay(EntityUid uid, SiliconLaw law, HashSet<string>? radioChannels)
+    public LawDisplay(EntityUid uid, SiliconLaw law, HashSet<ProtoId<RadioChannelPrototype>>? radioChannels)
     {
         RobustXamlLoader.Load(this);
         IoCManager.InjectDependencies(this);
index c5ae1ce49411a09a51a85c4eed873f202dea3e44..db713e92b3fe0e48ef3c2b43c9291ed05bab3037 100644 (file)
@@ -1,6 +1,6 @@
-using Content.Server.Radio.Components;
-using Content.Shared.Implants;
+using Content.Shared.Implants;
 using Content.Shared.Implants.Components;
+using Content.Shared.Radio.Components;
 
 namespace Content.Server.Implants;
 
diff --git a/Content.Server/Radio/Components/IntrinsicRadioTransmitterComponent.cs b/Content.Server/Radio/Components/IntrinsicRadioTransmitterComponent.cs
deleted file mode 100644 (file)
index 13cc090..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-using Content.Server.Chat.Systems;
-using Content.Shared.Chat;
-using Content.Shared.Radio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
-
-namespace Content.Server.Radio.Components;
-
-/// <summary>
-///     This component allows an entity to directly translate spoken text into radio messages (effectively an intrinsic
-///     radio headset).
-/// </summary>
-[RegisterComponent]
-public sealed partial class IntrinsicRadioTransmitterComponent : Component
-{
-    [DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
-    public HashSet<string> Channels = new() { SharedChatSystem.CommonChannel };
-}
diff --git a/Content.Server/Radio/Components/RadioMicrophoneComponent.cs b/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
deleted file mode 100644 (file)
index af01f86..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-using Content.Server.Radio.EntitySystems;
-using Content.Shared.Chat;
-using Content.Shared.Radio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
-
-namespace Content.Server.Radio.Components;
-
-/// <summary>
-///     Listens for local chat messages and relays them to some radio frequency
-/// </summary>
-[RegisterComponent]
-[Access(typeof(RadioDeviceSystem))]
-public sealed partial class RadioMicrophoneComponent : Component
-{
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("broadcastChannel", customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
-    public string BroadcastChannel = SharedChatSystem.CommonChannel;
-
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("listenRange")]
-    public int ListenRange  = 4;
-
-    [DataField("enabled")]
-    public bool Enabled = false;
-
-    [DataField("powerRequired")]
-    public bool PowerRequired = false;
-
-    /// <summary>
-    /// Whether or not interacting with this entity
-    /// toggles it on or off.
-    /// </summary>
-    [DataField("toggleOnInteract")]
-    public bool ToggleOnInteract = true;
-
-    /// <summary>
-    /// Whether or not the speaker must have an
-    /// unobstructed path to the radio to speak
-    /// </summary>
-    [DataField("unobstructedRequired")]
-    public bool UnobstructedRequired = false;
-}
diff --git a/Content.Server/Radio/Components/RadioSpeakerComponent.cs b/Content.Server/Radio/Components/RadioSpeakerComponent.cs
deleted file mode 100644 (file)
index 150e903..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-using Content.Server.Radio.EntitySystems;
-using Content.Shared.Chat;
-using Content.Shared.Radio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
-
-namespace Content.Server.Radio.Components;
-
-/// <summary>
-///     Listens for radio messages and relays them to local chat.
-/// </summary>
-[RegisterComponent]
-[Access(typeof(RadioDeviceSystem))]
-public sealed partial class RadioSpeakerComponent : Component
-{
-    /// <summary>
-    /// Whether or not interacting with this entity
-    /// toggles it on or off.
-    /// </summary>
-    [DataField("toggleOnInteract")]
-    public bool ToggleOnInteract = true;
-
-    [DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
-    public HashSet<string> Channels = new () { SharedChatSystem.CommonChannel };
-
-    [DataField("enabled")]
-    public bool Enabled;
-}
index e3f80703110b03995afdc6e7d2a19cbbcd4c14b9..91abc9efd5272f41afbf2bf8f4dc1bb7d9e6926e 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Server.Chat.Systems;
 using Content.Server.Emp;
-using Content.Server.Radio.Components;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Radio;
 using Content.Shared.Radio.Components;
index f052c460f50ad6b97d18d6722a96a30944248e1b..b0f38c92fae04fc6f310e2034304029cc65448d3 100644 (file)
@@ -3,7 +3,7 @@ using Content.Server.Chat.Systems;
 using Content.Server.Interaction;
 using Content.Server.Popups;
 using Content.Server.Power.EntitySystems;
-using Content.Server.Radio.Components;
+using Content.Shared.Radio.Components;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Power;
@@ -11,15 +11,15 @@ using Content.Shared.Radio;
 using Content.Shared.Speech;
 using Content.Shared.Speech.Components;
 using Content.Shared.Chat;
-using Content.Shared.Radio.Components;
 using Robust.Shared.Prototypes;
+using Content.Shared.Radio.EntitySystems;
 
 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
+public sealed class RadioDeviceSystem : SharedRadioDeviceSystem
 {
     [Dependency] private readonly IPrototypeManager _protoMan = default!;
     [Dependency] private readonly PopupSystem _popup = default!;
@@ -100,15 +100,6 @@ public sealed class RadioDeviceSystem : EntitySystem
         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)
@@ -116,7 +107,8 @@ public sealed class RadioDeviceSystem : EntitySystem
         SetMicrophoneEnabled(uid, null, false, true, component);
     }
 
-    public void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null)
+
+    public override void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null)
     {
         if (!Resolve(uid, ref component, false))
             return;
@@ -140,34 +132,6 @@ public sealed class RadioDeviceSystem : EntitySystem
             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)
@@ -277,9 +241,9 @@ public sealed class RadioDeviceSystem : EntitySystem
         }
 
         if (TryComp<RadioMicrophoneComponent>(ent, out var mic))
-            mic.BroadcastChannel = channel;
+            mic.BroadcastChannel = channel.Value;
         if (TryComp<RadioSpeakerComponent>(ent, out var speaker))
-            speaker.Channels = new() { channel };
+            speaker.Channels = new() { channel.Value };
         Dirty(ent);
     }
 }
index 6af52b980fccffaddfac3112552514c09e8a33cd..740e6b10303a7e0ac32cad7db0a3be13bc211dd4 100644 (file)
@@ -1,7 +1,6 @@
 using Content.Server.Administration.Logs;
 using Content.Server.Chat.Systems;
 using Content.Server.Power.Components;
-using Content.Server.Radio.Components;
 using Content.Shared.Chat;
 using Content.Shared.Database;
 using Content.Shared.Radio;
index e90acf1b80db6b1dbb9490c73c6e8092e896c237..b59331184069c6cec9f5fe07d256d98d53ce2c51 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.Inventory;
-using Content.Server.Radio.Components;
 using Content.Shared.Inventory;
+using Content.Shared.Radio.Components;
 using Content.Shared.Silicons.Borgs;
 using Content.Shared.Silicons.Borgs.Components;
 using Robust.Shared.Prototypes;
index 653ed6bac20ade00ce0e2f84adaccd8cc83450d1..79b51f22216435aa9918cd4b964757195ef9ae10 100644 (file)
@@ -1,7 +1,6 @@
 using System.Linq;
 using Content.Server.Administration;
 using Content.Server.Chat.Managers;
-using Content.Server.Radio.Components;
 using Content.Server.Station.Systems;
 using Content.Shared.Administration;
 using Content.Shared.Chat;
@@ -9,6 +8,7 @@ using Content.Shared.Emag.Systems;
 using Content.Shared.GameTicking;
 using Content.Shared.Mind;
 using Content.Shared.Mind.Components;
+using Content.Shared.Radio.Components;
 using Content.Shared.Roles;
 using Content.Shared.Roles.Components;
 using Content.Shared.Silicons.Laws;
index b8b1c19e84b71c42ec71fdd19b2fd289abf0492b..f9f1d9e92eae33805869199a8ed3f29eb474e0e0 100644 (file)
@@ -1,11 +1,11 @@
 using Content.Server.Chat.Systems;
-using Content.Shared.Radio;
-using Content.Server.Radio.Components;
+using Content.Shared.Radio.Components;
 using Content.Server.Radio.EntitySystems;
 using Content.Server.Speech.Components;
 using Content.Server.Wires;
-using Content.Shared.Wires;
+using Content.Shared.Radio;
 using Content.Shared.Speech;
+using Content.Shared.Wires;
 using Robust.Shared.Prototypes;
 
 namespace Content.Server.Speech;
index bdb14168bb8b37b370e92820189867e7f916c941..00f6b7bbd1addd6eccce45a6e8bee92b7804f336 100644 (file)
@@ -1,9 +1,9 @@
 using Content.Server.Chat.Systems;
-using Content.Server.Radio.Components;
 using Content.Server.Vocalization.Components;
 using Content.Shared.Chat;
 using Content.Shared.Inventory;
 using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 
@@ -42,9 +42,9 @@ public sealed partial class RadioVocalizationSystem : EntitySystem
     /// Selects a random radio channel from all ActiveRadio entities in a given entity's inventory
     /// If no channels are found, this returns false and sets channel to an empty string
     /// </summary>
-    private bool TryPickRandomRadioChannel(EntityUid entity, out string channel)
+    private bool TryPickRandomRadioChannel(EntityUid entity, out ProtoId<RadioChannelPrototype> channel)
     {
-        HashSet<string> potentialChannels = [];
+        HashSet<ProtoId<RadioChannelPrototype>> potentialChannels = [];
 
         // we don't have to check if this entity has an inventory. GetHandOrInventoryEntities will not yield anything
         // if an entity has no inventory or inventory slots
similarity index 57%
rename from Content.Server/Radio/Components/ActiveRadioComponent.cs
rename to Content.Shared/Radio/Components/ActiveRadioComponent.cs
index b0a2adbf9179fef9622fb280bc8b17c9021be1cd..25199b54e607fda580b9811342b09960afbcdb25 100644 (file)
@@ -1,30 +1,30 @@
-using Content.Shared.Radio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
 
-namespace Content.Server.Radio.Components;
+namespace Content.Shared.Radio.Components;
 
 /// <summary>
 ///     This component is required to receive radio message events.
 /// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
 public sealed partial class ActiveRadioComponent : Component
 {
     /// <summary>
     ///     The channels that this radio is listening on.
     /// </summary>
-    [DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
-    public HashSet<string> Channels = new();
+    [DataField]
+    public HashSet<ProtoId<RadioChannelPrototype>> Channels = new();
 
     /// <summary>
     /// A toggle for globally receiving all radio channels.
     /// Overrides <see cref="Channels"/>
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    [DataField]
     public bool ReceiveAllChannels;
 
     /// <summary>
     ///     If this radio can hear all messages on all maps
     /// </summary>
-    [DataField("globalReceive")]
+    [DataField]
     public bool GlobalReceive = false;
 }
index b7558d98252ec8fb440c5cc05151ca6767376862..54b77b5092a501de2361c217aa3a653cb22f69fd 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Shared.Chat;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Radio.Components;
 
@@ -8,15 +8,15 @@ namespace Content.Shared.Radio.Components;
 ///     This component is currently used for providing access to channels for "HeadsetComponent"s.
 ///     It should be used for intercoms and other radios in future.
 /// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
 public sealed partial class EncryptionKeyComponent : Component
 {
-    [DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
-    public HashSet<string> Channels = new();
+    [DataField]
+    public HashSet<ProtoId<RadioChannelPrototype>> Channels = new();
 
     /// <summary>
     ///     This is the channel that will be used when using the default/department prefix (<see cref="SharedChatSystem.DefaultChannelKey"/>).
     /// </summary>
-    [DataField("defaultChannel", customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
-    public string? DefaultChannel;
+    [DataField]
+    public ProtoId<RadioChannelPrototype>? DefaultChannel;
 }
index bd49acf9090696baa1703ac98a88e8bdf9ebf379..5067fe8c0c03e1ff725940b57e8537187a48d3d5 100644 (file)
@@ -2,40 +2,36 @@ using Content.Shared.Chat;
 using Content.Shared.Tools;
 using Robust.Shared.Audio;
 using Robust.Shared.Containers;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Radio.Components;
 
 /// <summary>
 ///     This component is by entities that can contain encryption keys
 /// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
 public sealed partial class EncryptionKeyHolderComponent : Component
 {
     /// <summary>
     ///     Whether or not encryption keys can be removed from the headset.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("keysUnlocked")]
+    [DataField]
     public bool KeysUnlocked = true;
 
     /// <summary>
     ///     The tool required to extract the encryption keys from the headset.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("keysExtractionMethod", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
-    public string KeysExtractionMethod = "Screwing";
+    [DataField]
+    public ProtoId<ToolQualityPrototype> KeysExtractionMethod = "Screwing";
 
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("keySlots")]
+    [DataField]
     public int KeySlots = 2;
 
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("keyExtractionSound")]
+    [DataField]
     public SoundSpecifier KeyExtractionSound = new SoundPathSpecifier("/Audio/Items/pistol_magout.ogg");
 
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("keyInsertionSound")]
+    [DataField]
     public SoundSpecifier KeyInsertionSound = new SoundPathSpecifier("/Audio/Items/pistol_magin.ogg");
 
     [ViewVariables]
@@ -46,7 +42,7 @@ public sealed partial class EncryptionKeyHolderComponent : Component
     ///     Combined set of radio channels provided by all contained keys.
     /// </summary>
     [ViewVariables]
-    public HashSet<string> Channels = new();
+    public HashSet<ProtoId<RadioChannelPrototype>> Channels = new();
 
     /// <summary>
     ///     This is the channel that will be used when using the default/department prefix (<see cref="SharedChatSystem.DefaultChannelKey"/>).
similarity index 73%
rename from Content.Server/Radio/Components/IntrinsicRadioReceiverComponent.cs
rename to Content.Shared/Radio/Components/IntrinsicRadioReceiverComponent.cs
index 416dfe39cb866b2b05ac625db01d172d2e856650..58d390f76d822e534ebb13c52006ec51a41049d6 100644 (file)
@@ -1,11 +1,11 @@
-namespace Content.Server.Radio.Components;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Radio.Components;
 
 /// <summary>
 ///     This component allows an entity to directly translate radio messages into chat messages. Note that this does not
 ///     automatically add an <see cref="ActiveRadioComponent"/>, which is required to receive radio messages on specific
 ///     channels.
 /// </summary>
-[RegisterComponent]
-public sealed partial class IntrinsicRadioReceiverComponent : Component
-{
-}
+[RegisterComponent, NetworkedComponent]
+public sealed partial class IntrinsicRadioReceiverComponent : Component;
diff --git a/Content.Shared/Radio/Components/IntrinsicRadioTransmitterComponent.cs b/Content.Shared/Radio/Components/IntrinsicRadioTransmitterComponent.cs
new file mode 100644 (file)
index 0000000..c9dca99
--- /dev/null
@@ -0,0 +1,16 @@
+using Content.Shared.Chat;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Radio.Components;
+
+/// <summary>
+///     This component allows an entity to directly translate spoken text into radio messages (effectively an intrinsic
+///     radio headset).
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class IntrinsicRadioTransmitterComponent : Component
+{
+    [DataField]
+    public HashSet<ProtoId<RadioChannelPrototype>> Channels = new() { SharedChatSystem.CommonChannel };
+}
diff --git a/Content.Shared/Radio/Components/RadioMicrophoneComponent.cs b/Content.Shared/Radio/Components/RadioMicrophoneComponent.cs
new file mode 100644 (file)
index 0000000..25f4100
--- /dev/null
@@ -0,0 +1,40 @@
+using Content.Shared.Radio.EntitySystems;
+using Content.Shared.Chat;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Radio.Components;
+
+/// <summary>
+///     Listens for local chat messages and relays them to some radio frequency
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedRadioDeviceSystem))]
+public sealed partial class RadioMicrophoneComponent : Component
+{
+    [DataField]
+    public ProtoId<RadioChannelPrototype> BroadcastChannel = SharedChatSystem.CommonChannel;
+
+    [DataField]
+    public int ListenRange = 4;
+
+    [DataField]
+    public bool Enabled = false;
+
+    [DataField]
+    public bool PowerRequired = false;
+
+    /// <summary>
+    /// Whether or not interacting with this entity
+    /// toggles it on or off.
+    /// </summary>
+    [DataField]
+    public bool ToggleOnInteract = true;
+
+    /// <summary>
+    /// Whether or not the speaker must have an
+    /// unobstructed path to the radio to speak
+    /// </summary>
+    [DataField]
+    public bool UnobstructedRequired = false;
+}
diff --git a/Content.Shared/Radio/Components/RadioSpeakerComponent.cs b/Content.Shared/Radio/Components/RadioSpeakerComponent.cs
new file mode 100644 (file)
index 0000000..b7dff28
--- /dev/null
@@ -0,0 +1,27 @@
+using Content.Shared.Radio.EntitySystems;
+using Content.Shared.Chat;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Radio.Components;
+
+/// <summary>
+///     Listens for radio messages and relays them to local chat.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedRadioDeviceSystem))]
+public sealed partial class RadioSpeakerComponent : Component
+{
+    /// <summary>
+    /// Whether or not interacting with this entity
+    /// toggles it on or off.
+    /// </summary>
+    [DataField]
+    public bool ToggleOnInteract = true;
+
+    [DataField]
+    public HashSet<ProtoId<RadioChannelPrototype>> Channels = new() { SharedChatSystem.CommonChannel };
+
+    [DataField, AutoNetworkedField]
+    public bool Enabled;
+}
similarity index 62%
rename from Content.Server/Radio/Components/WearingHeadsetComponent.cs
rename to Content.Shared/Radio/Components/WearingHeadsetComponent.cs
index 2d68b44a3c88c3a33fdde95d83ccd8083e9786da..974044261bb05146c928b293a9418a5a56e0196d 100644 (file)
@@ -1,13 +1,13 @@
-using Content.Server.Radio.EntitySystems;
+using Robust.Shared.GameStates;
 
-namespace Content.Server.Radio.Components;
+namespace Content.Shared.Radio.Components;
 
 /// <summary>
 ///     This component is used to tag players that are currently wearing an ACTIVE headset.
 /// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
 public sealed partial class WearingHeadsetComponent : Component
 {
-    [DataField("headset")]
+    [DataField]
     public EntityUid Headset;
 }
index b7ceba6ee9b4f8b8d67cb8cda8407a1a80c8a0f3..0b12bbc53f3281b200fa3a4e8ffd42cc13eca02c 100644 (file)
@@ -207,7 +207,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem
     /// <param name="channels">HashSet of channels in headset, encryptionkey or etc.</param>
     /// <param name="protoManager">IPrototypeManager for getting prototypes of channels with their variables.</param>
     /// <param name="channelFTLPattern">String that provide id of pattern in .ftl files to format channel with variables of it.</param>
-    public void AddChannelsExamine(HashSet<string> channels, string? defaultChannel, ExaminedEvent examineEvent, IPrototypeManager protoManager, string channelFTLPattern)
+    public void AddChannelsExamine(HashSet<ProtoId<RadioChannelPrototype>> channels, string? defaultChannel, ExaminedEvent examineEvent, IPrototypeManager protoManager, string channelFTLPattern)
     {
         RadioChannelPrototype? proto;
         foreach (var id in channels)
diff --git a/Content.Shared/Radio/EntitySystems/SharedRadioDeviceSystem.cs b/Content.Shared/Radio/EntitySystems/SharedRadioDeviceSystem.cs
new file mode 100644 (file)
index 0000000..715310f
--- /dev/null
@@ -0,0 +1,53 @@
+using Content.Shared.Popups;
+using Content.Shared.Radio.Components;
+
+namespace Content.Shared.Radio.EntitySystems;
+
+public abstract class SharedRadioDeviceSystem : EntitySystem
+{
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+
+    #region Toggling
+    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);
+    }
+
+    public virtual void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null) { }
+
+    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;
+        Dirty(uid, component);
+
+        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
+}
+
index 0fb9c5920fa7590d509902758c10aa907e81a92e..834d2eb77805e63d8d7f5eac4cf96ef614a5b987 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Actions;
+using Content.Shared.Radio;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
@@ -51,9 +52,9 @@ public enum SiliconLawsUiKey : byte
 public sealed class SiliconLawBuiState : BoundUserInterfaceState
 {
     public List<SiliconLaw> Laws;
-    public HashSet<string>? RadioChannels;
+    public HashSet<ProtoId<RadioChannelPrototype>>? RadioChannels;
 
-    public SiliconLawBuiState(List<SiliconLaw> laws, HashSet<string>? radioChannels)
+    public SiliconLawBuiState(List<SiliconLaw> laws, HashSet<ProtoId<RadioChannelPrototype>>? radioChannels)
     {
         Laws = laws;
         RadioChannels = radioChannels;