From: Slava0135 <40753025+Slava0135@users.noreply.github.com>
Date: Fri, 14 Apr 2023 19:50:19 +0000 (+0300)
Subject: Add radio jammer (#14369)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=7886d27ddab40cc7ef9ca90547418fa0629c06cc;p=space-station-14.git
Add radio jammer (#14369)
---
diff --git a/Content.Server/Radio/Components/ActiveRadioJammerComponent.cs b/Content.Server/Radio/Components/ActiveRadioJammerComponent.cs
new file mode 100644
index 0000000000..e608f0be7a
--- /dev/null
+++ b/Content.Server/Radio/Components/ActiveRadioJammerComponent.cs
@@ -0,0 +1,12 @@
+using Content.Server.Radio.EntitySystems;
+
+namespace Content.Server.Radio.Components;
+
+///
+/// Prevents all radio in range from sending messages
+///
+[RegisterComponent]
+[Access(typeof(JammerSystem))]
+public sealed class ActiveRadioJammerComponent : Component
+{
+}
diff --git a/Content.Server/Radio/Components/RadioJammerComponent.cs b/Content.Server/Radio/Components/RadioJammerComponent.cs
new file mode 100644
index 0000000000..ef451b0630
--- /dev/null
+++ b/Content.Server/Radio/Components/RadioJammerComponent.cs
@@ -0,0 +1,20 @@
+using Content.Server.Radio.EntitySystems;
+
+namespace Content.Server.Radio.Components;
+
+///
+/// When activated () prevents from sending messages in range
+///
+[RegisterComponent]
+[Access(typeof(JammerSystem))]
+public sealed class RadioJammerComponent : Component
+{
+ [DataField("range"), ViewVariables(VVAccess.ReadWrite)]
+ public float Range = 8f;
+
+ ///
+ /// Power usage per second when enabled
+ ///
+ [DataField("wattage"), ViewVariables(VVAccess.ReadWrite)]
+ public float Wattage = 6f;
+}
diff --git a/Content.Server/Radio/EntitySystems/JammerSystem.cs b/Content.Server/Radio/EntitySystems/JammerSystem.cs
new file mode 100644
index 0000000000..d3e1869bd5
--- /dev/null
+++ b/Content.Server/Radio/EntitySystems/JammerSystem.cs
@@ -0,0 +1,91 @@
+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(OnActivate);
+ SubscribeLocalEvent(OnPowerCellChanged);
+ SubscribeLocalEvent(OnExamine);
+ SubscribeLocalEvent(OnRadioSendAttempt);
+ }
+
+ public override void Update(float frameTime)
+ {
+ var query = AllEntityQuery();
+ while (query.MoveNext(out var uid, out var _, out var jam))
+ {
+ if (_powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
+ !battery.TryUseCharge(jam.Wattage * frameTime))
+ {
+ RemComp(uid);
+ }
+ }
+ }
+
+ private void OnActivate(EntityUid uid, RadioJammerComponent comp, ActivateInWorldEvent args)
+ {
+ var activated = !HasComp(uid) &&
+ _powerCell.TryGetBatteryFromSlot(uid, out var battery) &&
+ battery.CurrentCharge > comp.Wattage;
+ if (activated)
+ {
+ EnsureComp(uid);
+ }
+ else
+ {
+ RemComp(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(uid);
+ }
+
+ private void OnExamine(EntityUid uid, RadioJammerComponent comp, ExaminedEvent args)
+ {
+ if (args.IsInDetailsRange)
+ {
+ var msg = HasComp(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();
+ while (query.MoveNext(out _, out _, out var jam, out var transform))
+ {
+ if (source.InRange(EntityManager, _transform, transform.Coordinates, jam.Range))
+ {
+ args.Cancelled = true;
+ return;
+ }
+ }
+ }
+}
diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs
index 6557d5edef..2e6b93be32 100644
--- a/Content.Server/Radio/EntitySystems/RadioSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs
@@ -78,6 +78,10 @@ public sealed class RadioSystem : EntitySystem
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(radioSource);
@@ -85,7 +89,7 @@ public sealed class RadioSystem : EntitySystem
var speakerQuery = GetEntityQuery();
var radioQuery = AllEntityQuery();
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;
diff --git a/Content.Server/Radio/RadioEvent.cs b/Content.Server/Radio/RadioEvent.cs
new file mode 100644
index 0000000000..69d764ffe6
--- /dev/null
+++ b/Content.Server/Radio/RadioEvent.cs
@@ -0,0 +1,30 @@
+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);
+
+///
+/// Use this event to cancel sending message per receiver
+///
+[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;
+}
+
+///
+/// Use this event to cancel sending message to every receiver
+///
+[ByRefEvent]
+public record struct RadioSendAttemptEvent(RadioChannelPrototype Channel, EntityUid RadioSource)
+{
+ public readonly RadioChannelPrototype Channel = Channel;
+ public readonly EntityUid RadioSource = RadioSource;
+ public bool Cancelled = false;
+}
diff --git a/Content.Server/Radio/RadioReceiveEvent.cs b/Content.Server/Radio/RadioReceiveEvent.cs
deleted file mode 100644
index abcee61025..0000000000
--- a/Content.Server/Radio/RadioReceiveEvent.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-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;
- }
-}
-
-///
-/// Use this event to cancel sending messages by doing various checks (e.g. range)
-///
-[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;
- }
-}
diff --git a/Resources/Locale/en-US/radio/components/radio-jammer-component.ftl b/Resources/Locale/en-US/radio/components/radio-jammer-component.ftl
new file mode 100644
index 0000000000..63c0913d61
--- /dev/null
+++ b/Resources/Locale/en-US/radio/components/radio-jammer-component.ftl
@@ -0,0 +1,7 @@
+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.
diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl
index e935cc982e..ea3943d676 100644
--- a/Resources/Locale/en-US/store/uplink-catalog.ftl
+++ b/Resources/Locale/en-US/store/uplink-catalog.ftl
@@ -93,6 +93,9 @@ uplink-hypopen-desc = A chemical hypospray disguised as a pen, capable of instan
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!
diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml
index 2b2a4c0467..f920c1ba57 100644
--- a/Resources/Prototypes/Catalog/uplink_catalog.yml
+++ b/Resources/Prototypes/Catalog/uplink_catalog.yml
@@ -332,6 +332,16 @@
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
diff --git a/Resources/Prototypes/Entities/Objects/Tools/jammer.yml b/Resources/Prototypes/Entities/Objects/Tools/jammer.yml
new file mode 100644
index 0000000000..00214917cb
--- /dev/null
+++ b/Resources/Prototypes/Entities/Objects/Tools/jammer.yml
@@ -0,0 +1,21 @@
+- 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
diff --git a/Resources/Textures/Objects/Devices/jammer.rsi/jammer.png b/Resources/Textures/Objects/Devices/jammer.rsi/jammer.png
new file mode 100644
index 0000000000..6de27ba924
Binary files /dev/null and b/Resources/Textures/Objects/Devices/jammer.rsi/jammer.png differ
diff --git a/Resources/Textures/Objects/Devices/jammer.rsi/meta.json b/Resources/Textures/Objects/Devices/jammer.rsi/meta.json
new file mode 100644
index 0000000000..2923d9ac63
--- /dev/null
+++ b/Resources/Textures/Objects/Devices/jammer.rsi/meta.json
@@ -0,0 +1,15 @@
+{
+ "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