Add radio jammer (#14369)
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
}
|
||||||
20
Content.Server/Radio/Components/RadioJammerComponent.cs
Normal file
20
Content.Server/Radio/Components/RadioJammerComponent.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
91
Content.Server/Radio/EntitySystems/JammerSystem.cs
Normal file
91
Content.Server/Radio/EntitySystems/JammerSystem.cs
Normal file
@@ -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<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,6 +78,10 @@ public sealed class RadioSystem : EntitySystem
|
|||||||
var chatMsg = new MsgChatMessage { Message = chat };
|
var chatMsg = new MsgChatMessage { Message = chat };
|
||||||
var ev = new RadioReceiveEvent(message, messageSource, channel, chatMsg);
|
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 sourceMapId = Transform(radioSource).MapID;
|
||||||
var hasActiveServer = HasActiveServer(sourceMapId, channel.ID);
|
var hasActiveServer = HasActiveServer(sourceMapId, channel.ID);
|
||||||
var hasMicro = HasComp<RadioMicrophoneComponent>(radioSource);
|
var hasMicro = HasComp<RadioMicrophoneComponent>(radioSource);
|
||||||
@@ -85,7 +89,7 @@ public sealed class RadioSystem : EntitySystem
|
|||||||
var speakerQuery = GetEntityQuery<RadioSpeakerComponent>();
|
var speakerQuery = GetEntityQuery<RadioSpeakerComponent>();
|
||||||
var radioQuery = AllEntityQuery<ActiveRadioComponent, TransformComponent>();
|
var radioQuery = AllEntityQuery<ActiveRadioComponent, TransformComponent>();
|
||||||
var sentAtLeastOnce = false;
|
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))
|
if (!radio.Channels.Contains(channel.ID))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
30
Content.Server/Radio/RadioEvent.cs
Normal file
30
Content.Server/Radio/RadioEvent.cs
Normal file
@@ -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);
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.
|
||||||
@@ -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-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-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
|
# Implants
|
||||||
uplink-storage-implanter-name = Storage Implanter
|
uplink-storage-implanter-name = Storage Implanter
|
||||||
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!
|
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!
|
||||||
|
|||||||
@@ -332,6 +332,16 @@
|
|||||||
categories:
|
categories:
|
||||||
- UplinkUtility
|
- UplinkUtility
|
||||||
|
|
||||||
|
- type: listing
|
||||||
|
id: UplinkRadioJammer
|
||||||
|
name: uplink-radio-jammer-name
|
||||||
|
description: uplink-radio-jammer-desc
|
||||||
|
productEntity: RadioJammer
|
||||||
|
cost:
|
||||||
|
Telecrystal: 5
|
||||||
|
categories:
|
||||||
|
- UplinkUtility
|
||||||
|
|
||||||
# Implants
|
# Implants
|
||||||
|
|
||||||
- type: listing
|
- type: listing
|
||||||
|
|||||||
21
Resources/Prototypes/Entities/Objects/Tools/jammer.yml
Normal file
21
Resources/Prototypes/Entities/Objects/Tools/jammer.yml
Normal file
@@ -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
|
||||||
BIN
Resources/Textures/Objects/Devices/jammer.rsi/jammer.png
Normal file
BIN
Resources/Textures/Objects/Devices/jammer.rsi/jammer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 495 B |
15
Resources/Textures/Objects/Devices/jammer.rsi/meta.json
Normal file
15
Resources/Textures/Objects/Devices/jammer.rsi/meta.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user