--- /dev/null
+using Content.Server.Radio.Components;
+using Content.Shared.Implants;
+using Content.Shared.Implants.Components;
+using Robust.Shared.Containers;
+
+namespace Content.Server.Implants;
+
+public sealed class RadioImplantSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<RadioImplantComponent, ImplantImplantedEvent>(OnImplantImplanted);
+ SubscribeLocalEvent<RadioImplantComponent, EntGotRemovedFromContainerMessage>(OnRemove);
+ }
+
+ /// <summary>
+ /// If implanted with a radio implant, installs the necessary intrinsic radio components
+ /// </summary>
+ private void OnImplantImplanted(Entity<RadioImplantComponent> ent, ref ImplantImplantedEvent args)
+ {
+ if (args.Implanted == null)
+ return;
+
+ var activeRadio = EnsureComp<ActiveRadioComponent>(args.Implanted.Value);
+ foreach (var channel in ent.Comp.RadioChannels)
+ {
+ if (activeRadio.Channels.Add(channel))
+ ent.Comp.ActiveAddedChannels.Add(channel);
+ }
+
+ EnsureComp<IntrinsicRadioReceiverComponent>(args.Implanted.Value);
+
+ var intrinsicRadioTransmitter = EnsureComp<IntrinsicRadioTransmitterComponent>(args.Implanted.Value);
+ foreach (var channel in ent.Comp.RadioChannels)
+ {
+ if (intrinsicRadioTransmitter.Channels.Add(channel))
+ ent.Comp.TransmitterAddedChannels.Add(channel);
+ }
+ }
+
+ /// <summary>
+ /// Removes intrinsic radio components once the Radio Implant is removed
+ /// </summary>
+ private void OnRemove(Entity<RadioImplantComponent> ent, ref EntGotRemovedFromContainerMessage args)
+ {
+ if (TryComp<ActiveRadioComponent>(args.Container.Owner, out var activeRadioComponent))
+ {
+ foreach (var channel in ent.Comp.ActiveAddedChannels)
+ {
+ activeRadioComponent.Channels.Remove(channel);
+ }
+ ent.Comp.ActiveAddedChannels.Clear();
+
+ if (activeRadioComponent.Channels.Count == 0)
+ {
+ RemCompDeferred<ActiveRadioComponent>(args.Container.Owner);
+ }
+ }
+
+ if (!TryComp<IntrinsicRadioTransmitterComponent>(args.Container.Owner, out var radioTransmitterComponent))
+ return;
+
+ foreach (var channel in ent.Comp.TransmitterAddedChannels)
+ {
+ radioTransmitterComponent.Channels.Remove(channel);
+ }
+ ent.Comp.TransmitterAddedChannels.Clear();
+
+ if (radioTransmitterComponent.Channels.Count == 0 || activeRadioComponent?.Channels.Count == 0)
+ {
+ RemCompDeferred<IntrinsicRadioTransmitterComponent>(args.Container.Owner);
+ }
+ }
+}
--- /dev/null
+using Content.Shared.Radio;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Implants.Components;
+
+/// <summary>
+/// Gives the user access to a given channel without the need for a headset.
+/// </summary>
+[RegisterComponent]
+public sealed partial class RadioImplantComponent : Component
+{
+ /// <summary>
+ /// The radio channel(s) to grant access to.
+ /// </summary>
+ [DataField(required: true)]
+ public HashSet<ProtoId<RadioChannelPrototype>> RadioChannels = new();
+
+ /// <summary>
+ /// The radio channels that have been added by the implant to a user's ActiveRadioComponent.
+ /// Used to track which channels were successfully added (not already in user)
+ /// </summary>
+ /// <remarks>
+ /// Should not be modified outside RadioImplantSystem.cs
+ /// </remarks>
+ [DataField]
+ public HashSet<ProtoId<RadioChannelPrototype>> ActiveAddedChannels = new();
+
+ /// <summary>
+ /// The radio channels that have been added by the implant to a user's IntrinsicRadioTransmitterComponent.
+ /// Used to track which channels were successfully added (not already in user)
+ /// </summary>
+ /// <remarks>
+ /// Should not be modified outside RadioImplantSystem.cs
+ /// </remarks>
+ [DataField]
+ public HashSet<ProtoId<RadioChannelPrototype>> TransmitterAddedChannels = new();
+}
uplink-micro-bomb-implanter-name = Micro Bomb Implanter
uplink-micro-bomb-implanter-desc = Explode on death or manual activation with this implant. Destroys the body with all equipment.
+uplink-radio-implanter-name = Radio Implanter
+uplink-radio-implanter-desc = Implants a Syndicate radio, allowing covert communication without a headset.
+
# Bundles
uplink-observation-kit-name = Observation Kit
uplink-observation-kit-desc = Includes surveillance camera monitor board and security hud disguised as sunglasses.
categories:
- UplinkImplants
+- type: listing
+ id: UplinkRadioImplanter
+ name: uplink-radio-implanter-name
+ description: uplink-radio-implanter-desc
+ icon: { sprite: /Textures/Objects/Devices/encryption_keys.rsi, state: synd_label }
+ productEntity: RadioImplanter
+ discountCategory: usualDiscounts
+ discountDownTo:
+ Telecrystal: 1
+ cost:
+ Telecrystal: 2
+ categories:
+ - UplinkImplants
+
- type: listing
id: UplinkMicroBombImplanter
name: uplink-micro-bomb-implanter-name
- type: Implanter
implant: FreedomImplant
+- type: entity
+ id: RadioImplanter
+ suffix: radio Syndicate
+ parent: BaseImplantOnlyImplanterSyndi
+ components:
+ - type: Implanter
+ implant: RadioImplant
+
- type: entity
id: UplinkImplanter
suffix: uplink
components:
- type: Implanter
implant: MindShieldImplant
+
+# Centcomm implanters
+
+- type: entity
+ id: RadioImplanterCentcomm
+ suffix: radio Centcomm
+ parent: BaseImplantOnlyImplanter
+ components:
+ - type: Implanter
+ implant: RadioImplantCentcomm
components:
- Cuffable # useless if you cant be cuffed
+- type: entity
+ parent: BaseSubdermalImplant
+ id: RadioImplant
+ name: radio implant
+ description: This implant grants access to the Syndicate channel without a headset.
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: SubdermalImplant
+ - type: RadioImplant
+ radioChannels:
+ - Syndicate
+
- type: entity
parent: [ BaseSubdermalImplant, StorePresetUplink ]
id: UplinkImplant
- type: Tag
tags:
- MindShield
+
+# Centcomm implants
+
+- type: entity
+ parent: BaseSubdermalImplant
+ id: RadioImplantCentcomm
+ name: radio implant
+ description: This implant grants access to the Centcomm channel without a headset. Only authorized for Centcomm employees.
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: SubdermalImplant
+ - type: RadioImplant
+ radioChannels:
+ - CentCom