--- /dev/null
+using Content.Server.Radio.EntitySystems;
+
+namespace Content.Server.Radio.Components;
+
+/// <summary>
+/// Prevents all radio in range from sending messages
+/// </summary>
+[RegisterComponent]
+[Access(typeof(JammerSystem))]
+public sealed class ActiveRadioJammerComponent : Component
+{
+}
--- /dev/null
+using Content.Server.Radio.EntitySystems;
+
+namespace Content.Server.Radio.Components;
+
+/// <summary>
+/// When activated (<see cref="ActiveRadioJammerComponent"/>) prevents from sending messages in range
+/// </summary>
+[RegisterComponent]
+[Access(typeof(JammerSystem))]
+public sealed class RadioJammerComponent : Component
+{
+ [DataField("range"), ViewVariables(VVAccess.ReadWrite)]
+ public float Range = 8f;
+
+ /// <summary>
+ /// Power usage per second when enabled
+ /// </summary>
+ [DataField("wattage"), ViewVariables(VVAccess.ReadWrite)]
+ public float Wattage = 6f;
+}
--- /dev/null
+using Content.Server.Popups;
+using Content.Server.PowerCell;
+using Content.Server.Radio.Components;
+using Content.Shared.Examine;
+using Content.Shared.Interaction;
+using Content.Shared.PowerCell.Components;
+
+namespace Content.Server.Radio.EntitySystems;
+
+public sealed class JammerSystem : EntitySystem
+{
+ [Dependency] private readonly PowerCellSystem _powerCell = default!;
+ [Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<RadioJammerComponent, ActivateInWorldEvent>(OnActivate);
+ SubscribeLocalEvent<ActiveRadioJammerComponent, PowerCellChangedEvent>(OnPowerCellChanged);
+ SubscribeLocalEvent<RadioJammerComponent, ExaminedEvent>(OnExamine);
+ SubscribeLocalEvent<RadioSendAttemptEvent>(OnRadioSendAttempt);
+ }
+
+ public override void Update(float frameTime)
+ {
+ var query = AllEntityQuery<ActiveRadioJammerComponent, RadioJammerComponent>();
+ while (query.MoveNext(out var uid, out var _, out var jam))
+ {
+ if (_powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
+ !battery.TryUseCharge(jam.Wattage * frameTime))
+ {
+ RemComp<ActiveRadioJammerComponent>(uid);
+ }
+ }
+ }
+
+ private void OnActivate(EntityUid uid, RadioJammerComponent comp, ActivateInWorldEvent args)
+ {
+ var activated = !HasComp<ActiveRadioJammerComponent>(uid) &&
+ _powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
+ battery.CurrentCharge > comp.Wattage;
+ if (activated)
+ {
+ EnsureComp<ActiveRadioJammerComponent>(uid);
+ }
+ else
+ {
+ RemComp<ActiveRadioJammerComponent>(uid);
+ }
+ var state = Loc.GetString(activated ? "radio-jammer-component-on-state" : "radio-jammer-component-off-state");
+ var message = Loc.GetString("radio-jammer-component-on-use", ("state", state));
+ _popup.PopupEntity(message, args.User, args.User);
+ args.Handled = true;
+ }
+
+ private void OnPowerCellChanged(EntityUid uid, ActiveRadioJammerComponent comp, PowerCellChangedEvent args)
+ {
+ if (args.Ejected)
+ RemComp<ActiveRadioJammerComponent>(uid);
+ }
+
+ private void OnExamine(EntityUid uid, RadioJammerComponent comp, ExaminedEvent args)
+ {
+ if (args.IsInDetailsRange)
+ {
+ var msg = HasComp<ActiveRadioJammerComponent>(uid)
+ ? Loc.GetString("radio-jammer-component-examine-on-state")
+ : Loc.GetString("radio-jammer-component-examine-off-state");
+ args.PushMarkup(msg);
+ if (_powerCell.TryGetBatteryFromSlot(uid, out var battery))
+ args.PushMarkup(Loc.GetString("radio-jammer-component-charge",
+ ("charge", (int) ((battery.CurrentCharge / battery.MaxCharge) * 100))));
+ }
+ }
+
+ private void OnRadioSendAttempt(ref RadioSendAttemptEvent args)
+ {
+ var source = Transform(args.RadioSource).Coordinates;
+ var query = AllEntityQuery<ActiveRadioJammerComponent, RadioJammerComponent, TransformComponent>();
+ while (query.MoveNext(out _, out _, out var jam, out var transform))
+ {
+ if (source.InRange(EntityManager, _transform, transform.Coordinates, jam.Range))
+ {
+ args.Cancelled = true;
+ return;
+ }
+ }
+ }
+}
var chatMsg = new MsgChatMessage { Message = chat };
var ev = new RadioReceiveEvent(message, messageSource, channel, chatMsg);
+ var sendAttemptEv = new RadioSendAttemptEvent(channel, radioSource);
+ RaiseLocalEvent(ref sendAttemptEv);
+ var canSend = !sendAttemptEv.Cancelled;
+
var sourceMapId = Transform(radioSource).MapID;
var hasActiveServer = HasActiveServer(sourceMapId, channel.ID);
var hasMicro = HasComp<RadioMicrophoneComponent>(radioSource);
var speakerQuery = GetEntityQuery<RadioSpeakerComponent>();
var radioQuery = AllEntityQuery<ActiveRadioComponent, TransformComponent>();
var sentAtLeastOnce = false;
- while (radioQuery.MoveNext(out var receiver, out var radio, out var transform))
+ while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform))
{
if (!radio.Channels.Contains(channel.ID))
continue;
--- /dev/null
+using Content.Shared.Chat;
+using Content.Shared.Radio;
+
+namespace Content.Server.Radio;
+
+[ByRefEvent]
+public readonly record struct RadioReceiveEvent(string Message, EntityUid MessageSource, RadioChannelPrototype Channel, MsgChatMessage ChatMsg);
+
+/// <summary>
+/// Use this event to cancel sending message per receiver
+/// </summary>
+[ByRefEvent]
+public record struct RadioReceiveAttemptEvent(RadioChannelPrototype Channel, EntityUid RadioSource, EntityUid RadioReceiver)
+{
+ public readonly RadioChannelPrototype Channel = Channel;
+ public readonly EntityUid RadioSource = RadioSource;
+ public readonly EntityUid RadioReceiver = RadioReceiver;
+ public bool Cancelled = false;
+}
+
+/// <summary>
+/// Use this event to cancel sending message to every receiver
+/// </summary>
+[ByRefEvent]
+public record struct RadioSendAttemptEvent(RadioChannelPrototype Channel, EntityUid RadioSource)
+{
+ public readonly RadioChannelPrototype Channel = Channel;
+ public readonly EntityUid RadioSource = RadioSource;
+ public bool Cancelled = false;
+}
+++ /dev/null
-using Content.Shared.Chat;
-using Content.Shared.Radio;
-
-namespace Content.Server.Radio;
-
-[ByRefEvent]
-public struct RadioReceiveEvent
-{
- public readonly string Message;
- public readonly EntityUid MessageSource;
- public readonly RadioChannelPrototype Channel;
- public readonly MsgChatMessage ChatMsg;
-
- public RadioReceiveEvent(string message, EntityUid messageSource, RadioChannelPrototype channel, MsgChatMessage chatMsg)
- {
- Message = message;
- MessageSource = messageSource;
- Channel = channel;
- ChatMsg = chatMsg;
- }
-}
-
-/// <summary>
-/// Use this event to cancel sending messages by doing various checks (e.g. range)
-/// </summary>
-[ByRefEvent]
-public struct RadioReceiveAttemptEvent
-{
- public readonly RadioChannelPrototype Channel;
- public readonly EntityUid RadioSource;
- public readonly EntityUid RadioReceiver;
-
- public bool Cancelled = false;
-
- public RadioReceiveAttemptEvent(RadioChannelPrototype channel, EntityUid radioSource, EntityUid radioReceiver)
- {
- Channel = channel;
- RadioSource = radioSource;
- RadioReceiver = radioReceiver;
- }
-}
--- /dev/null
+radio-jammer-component-on-use = The jammer is now {$state}.
+radio-jammer-component-on-state = on
+radio-jammer-component-off-state = off
+
+radio-jammer-component-examine-on-state = The light is currently [color=darkgreen]on[/color].
+radio-jammer-component-examine-off-state = The light is currently [color=darkred]off[/color].
+radio-jammer-component-charge = The battery is [color=yellow]{$charge}%[/color] full.
uplink-voice-mask-name = Voice Mask
uplink-voice-mask-desc = A gas mask that lets you adjust your voice to whoever you can think of. Also utilizes cutting-edge chameleon technology.
+uplink-radio-jammer-name = Radio Jammer
+uplink-radio-jammer-desc = This device will disrupt any nearby outgoing radio communication when activated.
+
# Implants
uplink-storage-implanter-name = Storage Implanter
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!
categories:
- UplinkUtility
+- type: listing
+ id: UplinkRadioJammer
+ name: uplink-radio-jammer-name
+ description: uplink-radio-jammer-desc
+ productEntity: RadioJammer
+ cost:
+ Telecrystal: 5
+ categories:
+ - UplinkUtility
+
# Implants
- type: listing
--- /dev/null
+- type: entity
+ name: radio jammer
+ parent: BaseItem
+ id: RadioJammer
+ description: This device will disrupt any nearby outgoing radio communication when activated.
+ components:
+ - type: Sprite
+ netsync: false
+ sprite: Objects/Devices/jammer.rsi
+ state: jammer
+ - type: RadioJammer
+ - type: PowerCellSlot
+ cellSlotId: cell_slot
+ - type: ContainerContainer
+ containers:
+ cell_slot: !type:ContainerSlot
+ - type: ItemSlots
+ slots:
+ cell_slot:
+ name: power-cell-slot-component-slot-name-default
+ startingItem: PowerCellMedium
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation/commit/c65da5a49477413310c81c460ea4b243a9f864dd",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "jammer",
+ "directions": 1
+ }
+ ]
+}
\ No newline at end of file