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