mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Predict intercom examine. (#43107)
* radiosystem is funny * review Co-authored-by: slarticodefast <slarticodefast@users.noreply.github.com> Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com> * dont forget your tests * dont forget your loc * networking Co-authored-by: Tayrtahn <Tayrtahn@users.noreply.github.com> * dont return because its breaks * uh * review Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com> * more predictions and cleanup * refactor: just me, being picky about whitespaces and commetns formatting --------- Co-authored-by: slarticodefast <slarticodefast@users.noreply.github.com> Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com> Co-authored-by: Tayrtahn <Tayrtahn@users.noreply.github.com> Co-authored-by: Tayrtahn <tayrtahn@gmail.com> Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com> Co-authored-by: Fildrance <fildrance@gmail.com>
This commit is contained in:
parent
0757710b46
commit
da8bf6df00
20 changed files with 471 additions and 351 deletions
|
|
@ -6,16 +6,12 @@ using Robust.Client.GameObjects;
|
|||
|
||||
namespace Content.Client.Radio.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class RadioDeviceSystem : SharedRadioDeviceSystem
|
||||
{
|
||||
[Dependency] private UserInterfaceSystem _ui = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<IntercomComponent, AfterAutoHandleStateEvent>(OnAfterHandleState);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnAfterHandleState(Entity<IntercomComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (_ui.TryGetOpenUi<IntercomBoundUserInterface>(ent.Owner, IntercomUiKey.Key, out var bui))
|
||||
|
|
|
|||
6
Content.Client/Radio/EntitySystems/RadioSystem.cs
Normal file
6
Content.Client/Radio/EntitySystems/RadioSystem.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
using Content.Shared.Radio.EntitySystems;
|
||||
|
||||
namespace Content.Client.Radio.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class RadioSystem : SharedRadioSystem;
|
||||
|
|
@ -2,12 +2,12 @@ using Content.Server.Administration.Logs;
|
|||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.Animals.Components;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Radio;
|
||||
using Content.Server.Vocalization.Systems;
|
||||
using Content.Shared.Animals.Components;
|
||||
using Content.Shared.Animals.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Radio;
|
||||
using Content.Server.SurveillanceCamera;
|
||||
using Content.Shared.Emp;
|
||||
using Content.Shared.Radio;
|
||||
|
||||
namespace Content.Server.Emp;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using Content.Shared.Radio;
|
||||
using Content.Shared.Radio.EntitySystems;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
|
||||
namespace Content.Server.Radio.EntitySystems;
|
||||
|
||||
|
|
@ -27,7 +29,7 @@ public sealed partial class JammerSystem : SharedJammerSystem
|
|||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private bool ShouldCancel(EntityUid sourceUid, int frequency)
|
||||
private bool ShouldCancel(EntityUid sourceUid, FixedPoint2 frequency)
|
||||
{
|
||||
var source = Transform(sourceUid).Coordinates;
|
||||
var query = EntityQueryEnumerator<ActiveRadioJammerComponent, RadioJammerComponent, TransformComponent>();
|
||||
|
|
|
|||
|
|
@ -1,248 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Radio.EntitySystems;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Radio.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles radio speakers and microphones (which together form a hand-held radio).
|
||||
/// </summary>
|
||||
public sealed partial class RadioDeviceSystem : SharedRadioDeviceSystem
|
||||
{
|
||||
[Dependency] private PopupSystem _popup = default!;
|
||||
[Dependency] private ChatSystem _chat = default!;
|
||||
[Dependency] private RadioSystem _radio = default!;
|
||||
[Dependency] private InteractionSystem _interaction = default!;
|
||||
[Dependency] private SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
// Used to prevent a shitter from using a bunch of radios to spam chat.
|
||||
private HashSet<(string, EntityUid, RadioChannelPrototype)> _recentlySent = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ComponentInit>(OnMicrophoneInit);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ActivateInWorldEvent>(OnActivateMicrophone);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ListenEvent>(OnListen);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ListenAttemptEvent>(OnAttemptListen);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
|
||||
SubscribeLocalEvent<RadioSpeakerComponent, ComponentInit>(OnSpeakerInit);
|
||||
SubscribeLocalEvent<RadioSpeakerComponent, ActivateInWorldEvent>(OnActivateSpeaker);
|
||||
SubscribeLocalEvent<RadioSpeakerComponent, RadioReceiveEvent>(OnReceiveRadio);
|
||||
|
||||
SubscribeLocalEvent<IntercomComponent, EncryptionChannelsChangedEvent>(OnIntercomEncryptionChannelsChanged);
|
||||
SubscribeLocalEvent<IntercomComponent, ToggleIntercomMicMessage>(OnToggleIntercomMic);
|
||||
SubscribeLocalEvent<IntercomComponent, ToggleIntercomSpeakerMessage>(OnToggleIntercomSpeaker);
|
||||
SubscribeLocalEvent<IntercomComponent, SelectIntercomChannelMessage>(OnSelectIntercomChannel);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
_recentlySent.Clear();
|
||||
}
|
||||
|
||||
|
||||
#region Component Init
|
||||
private void OnMicrophoneInit(EntityUid uid, RadioMicrophoneComponent component, ComponentInit args)
|
||||
{
|
||||
if (component.Enabled)
|
||||
EnsureComp<ActiveListenerComponent>(uid).Range = component.ListenRange;
|
||||
else
|
||||
RemCompDeferred<ActiveListenerComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnSpeakerInit(EntityUid uid, RadioSpeakerComponent component, ComponentInit args)
|
||||
{
|
||||
if (component.Enabled)
|
||||
EnsureComp<ActiveRadioComponent>(uid).Channels.UnionWith(component.Channels);
|
||||
else
|
||||
RemCompDeferred<ActiveRadioComponent>(uid);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Toggling
|
||||
private void OnActivateMicrophone(EntityUid uid, RadioMicrophoneComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (!args.Complex)
|
||||
return;
|
||||
|
||||
if (!component.ToggleOnInteract)
|
||||
return;
|
||||
|
||||
ToggleRadioMicrophone(uid, args.User, args.Handled, component);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnActivateSpeaker(EntityUid uid, RadioSpeakerComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (!args.Complex)
|
||||
return;
|
||||
|
||||
if (!component.ToggleOnInteract)
|
||||
return;
|
||||
|
||||
ToggleRadioSpeaker(uid, args.User, args.Handled, component);
|
||||
args.Handled = true;
|
||||
}
|
||||
private void OnPowerChanged(EntityUid uid, RadioMicrophoneComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
if (args.Powered)
|
||||
return;
|
||||
SetMicrophoneEnabled(uid, null, false, true, component);
|
||||
}
|
||||
|
||||
|
||||
public override void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
component.Enabled = enabled;
|
||||
|
||||
if (!quiet && user != null)
|
||||
{
|
||||
var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state");
|
||||
var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
|
||||
_popup.PopupEntity(message, user.Value, user.Value);
|
||||
}
|
||||
|
||||
_appearance.SetData(uid, RadioDeviceVisuals.Broadcasting, component.Enabled);
|
||||
if (component.Enabled)
|
||||
EnsureComp<ActiveListenerComponent>(uid).Range = component.ListenRange;
|
||||
else
|
||||
RemCompDeferred<ActiveListenerComponent>(uid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnExamine(EntityUid uid, RadioMicrophoneComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
var proto = ProtoMan.Index<RadioChannelPrototype>(component.BroadcastChannel);
|
||||
|
||||
using (args.PushGroup(nameof(RadioMicrophoneComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency)));
|
||||
args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine",
|
||||
("channel", proto.LocalizedName)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenEvent args)
|
||||
{
|
||||
if (HasComp<RadioSpeakerComponent>(args.Source))
|
||||
return; // no feedback loops please.
|
||||
|
||||
var channel = ProtoMan.Index<RadioChannelPrototype>(component.BroadcastChannel)!;
|
||||
if (_recentlySent.Add((args.Message, args.Source, channel)))
|
||||
_radio.SendRadioMessage(args.Source, args.Message, channel, uid);
|
||||
}
|
||||
|
||||
private void OnAttemptListen(EntityUid uid, RadioMicrophoneComponent component, ListenAttemptEvent args)
|
||||
{
|
||||
if (component.PowerRequired && !this.IsPowered(uid, EntityManager)
|
||||
|| component.UnobstructedRequired && !_interaction.InRangeUnobstructed(args.Source, uid, 0))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, ref RadioReceiveEvent args)
|
||||
{
|
||||
if (uid == args.RadioSource)
|
||||
return;
|
||||
|
||||
var nameEv = new TransformSpeakerNameEvent(args.MessageSource, Name(args.MessageSource));
|
||||
RaiseLocalEvent(args.MessageSource, nameEv);
|
||||
|
||||
var name = Loc.GetString("speech-name-relay",
|
||||
("speaker", Name(uid)),
|
||||
("originalName", nameEv.VoiceName));
|
||||
|
||||
// log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
|
||||
_chat.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Whisper, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false);
|
||||
}
|
||||
|
||||
private void OnIntercomEncryptionChannelsChanged(Entity<IntercomComponent> ent, ref EncryptionChannelsChangedEvent args)
|
||||
{
|
||||
ent.Comp.SupportedChannels = args.Component.Channels.Select(p => new ProtoId<RadioChannelPrototype>(p)).ToList();
|
||||
|
||||
var channel = args.Component.DefaultChannel;
|
||||
if (ent.Comp.CurrentChannel != null && ent.Comp.SupportedChannels.Contains(ent.Comp.CurrentChannel.Value))
|
||||
channel = ent.Comp.CurrentChannel;
|
||||
|
||||
SetIntercomChannel(ent, channel);
|
||||
}
|
||||
|
||||
private void OnToggleIntercomMic(Entity<IntercomComponent> ent, ref ToggleIntercomMicMessage args)
|
||||
{
|
||||
if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
|
||||
return;
|
||||
|
||||
SetMicrophoneEnabled(ent, args.Actor, args.Enabled, true);
|
||||
ent.Comp.MicrophoneEnabled = args.Enabled;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnToggleIntercomSpeaker(Entity<IntercomComponent> ent, ref ToggleIntercomSpeakerMessage args)
|
||||
{
|
||||
if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
|
||||
return;
|
||||
|
||||
SetSpeakerEnabled(ent, args.Actor, args.Enabled, true);
|
||||
ent.Comp.SpeakerEnabled = args.Enabled;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnSelectIntercomChannel(Entity<IntercomComponent> ent, ref SelectIntercomChannelMessage args)
|
||||
{
|
||||
if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
|
||||
return;
|
||||
|
||||
if (!ProtoMan.HasIndex<RadioChannelPrototype>(args.Channel) || !ent.Comp.SupportedChannels.Contains(args.Channel))
|
||||
return;
|
||||
|
||||
SetIntercomChannel(ent, args.Channel);
|
||||
}
|
||||
|
||||
private void SetIntercomChannel(Entity<IntercomComponent> ent, ProtoId<RadioChannelPrototype>? channel)
|
||||
{
|
||||
ent.Comp.CurrentChannel = channel;
|
||||
|
||||
if (channel == null)
|
||||
{
|
||||
SetSpeakerEnabled(ent, null, false);
|
||||
SetMicrophoneEnabled(ent, null, false);
|
||||
ent.Comp.MicrophoneEnabled = false;
|
||||
ent.Comp.SpeakerEnabled = false;
|
||||
Dirty(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp<RadioMicrophoneComponent>(ent, out var mic))
|
||||
mic.BroadcastChannel = channel.Value;
|
||||
if (TryComp<RadioSpeakerComponent>(ent, out var speaker))
|
||||
speaker.Channels = new() { channel.Value };
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class RadioDeviceSystem : SharedRadioDeviceSystem;
|
||||
|
|
|
|||
|
|
@ -7,21 +7,19 @@ using Content.Shared.Chat;
|
|||
using Content.Shared.Database;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Radio.EntitySystems;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Radio.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles intrinsic radios and the general process of converting radio messages into chat messages.
|
||||
/// </summary>
|
||||
public sealed partial class RadioSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public sealed partial class RadioSystem : SharedRadioSystem
|
||||
{
|
||||
[Dependency] private INetManager _netMan = default!;
|
||||
[Dependency] private IReplayRecordingManager _replay = default!;
|
||||
|
|
@ -74,20 +72,8 @@ public sealed partial class RadioSystem : EntitySystem
|
|||
_netMan.ServerSendMessage(msg, actor.PlayerSession.Channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send radio message to all active radio listeners
|
||||
/// </summary>
|
||||
public void SendRadioMessage(EntityUid messageSource, string message, ProtoId<RadioChannelPrototype> channel, EntityUid radioSource, bool escapeMarkup = true)
|
||||
{
|
||||
SendRadioMessage(messageSource, message, ProtoMan.Index(channel), radioSource, escapeMarkup: escapeMarkup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send radio message to all active radio listeners
|
||||
/// </summary>
|
||||
/// <param name="messageSource">Entity that spoke the message</param>
|
||||
/// <param name="radioSource">Entity that picked up the message and will send it, e.g. headset</param>
|
||||
public void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, bool escapeMarkup = true)
|
||||
/// <inheritdoc/>
|
||||
public override void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, bool escapeMarkup = true)
|
||||
{
|
||||
// TODO if radios ever garble / modify messages, feedback-prevention needs to be handled better than this.
|
||||
if (!_messages.Add(message))
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Radio;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.StationEvents.Events;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ public sealed partial class RadioJammerComponent : Component
|
|||
/// Frequencies that are NOT jammed by this jammer.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<int> FrequenciesExcluded = [];
|
||||
public HashSet<FixedPoint2> FrequenciesExcluded = [];
|
||||
|
||||
/// <summary>
|
||||
/// Index of the currently selected setting.
|
||||
|
|
|
|||
|
|
@ -6,35 +6,45 @@ using Robust.Shared.Prototypes;
|
|||
namespace Content.Shared.Radio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Listens for local chat messages and relays them to some radio frequency
|
||||
/// Listens for local chat messages and relays them to some radio frequency.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SharedRadioDeviceSystem))]
|
||||
public sealed partial class RadioMicrophoneComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
/// <summary>
|
||||
/// Radio channel on which local speech is broadcast.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<RadioChannelPrototype> BroadcastChannel = SharedChatSystem.CommonChannel;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance from the microphone at which speech is heard.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int ListenRange = 4;
|
||||
|
||||
[DataField]
|
||||
public bool Enabled = false;
|
||||
|
||||
[DataField]
|
||||
public bool PowerRequired = false;
|
||||
/// <summary>
|
||||
/// Whether the microphone is currently broadcasting.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not interacting with this entity
|
||||
/// toggles it on or off.
|
||||
/// Whether the microphone requires power to operate.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PowerRequired;
|
||||
|
||||
/// <summary>
|
||||
/// Whether interacting with this entity toggles it on/off, or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool ToggleOnInteract = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the speaker must have an
|
||||
/// unobstructed path to the radio to speak
|
||||
/// Whether the speaker must have an unobstructed path to the radio to speak, or now.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool UnobstructedRequired = false;
|
||||
public bool UnobstructedRequired;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,22 +6,27 @@ using Robust.Shared.Prototypes;
|
|||
namespace Content.Shared.Radio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Listens for radio messages and relays them to local chat.
|
||||
/// Listens for radio messages and relays them to local chat.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SharedRadioDeviceSystem))]
|
||||
public sealed partial class RadioSpeakerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not interacting with this entity
|
||||
/// toggles it on or off.
|
||||
/// Whether interacting with this entity toggles it on/off, or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool ToggleOnInteract = true;
|
||||
|
||||
[DataField]
|
||||
/// <summary>
|
||||
/// Radio channels from which messages are received.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public HashSet<ProtoId<RadioChannelPrototype>> Channels = new() { SharedChatSystem.CommonChannel };
|
||||
|
||||
/// <summary>
|
||||
/// Whether the speaker is currently receiving radio messages.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Enabled;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem
|
|||
("color", proto.Color),
|
||||
("key", key),
|
||||
("id", proto.LocalizedName),
|
||||
("freq", proto.Frequency / 10f)));
|
||||
("freq", proto.Frequency)));
|
||||
}
|
||||
|
||||
if (defaultChannel != null && ProtoMan.TryIndex(defaultChannel, out proto))
|
||||
|
|
|
|||
|
|
@ -1,53 +1,360 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Speech.Components;
|
||||
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Radio.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles radio speakers and microphones (which together form a hand-held radio).
|
||||
/// </summary>
|
||||
public abstract partial class SharedRadioDeviceSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedPopupSystem _popup = default!;
|
||||
[Dependency] private SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private SharedPopupSystem _popup = default!;
|
||||
[Dependency] private SharedChatSystem _chat = default!;
|
||||
[Dependency] private SharedRadioSystem _radio = default!;
|
||||
[Dependency] private SharedPowerReceiverSystem _power = default!;
|
||||
[Dependency] private SharedInteractionSystem _interaction = default!;
|
||||
|
||||
// Used to prevent a shitter from using a bunch of radios to spam chat.
|
||||
private readonly HashSet<(string, EntityUid, RadioChannelPrototype)> _recentlySent = [];
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
_recentlySent.Clear();
|
||||
}
|
||||
|
||||
#region Component Init
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMicrophoneInit(Entity<RadioMicrophoneComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (ent.Comp.Enabled)
|
||||
EnsureComp<ActiveListenerComponent>(ent).Range = ent.Comp.ListenRange;
|
||||
else
|
||||
RemCompDeferred<ActiveListenerComponent>(ent);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnSpeakerInit(Entity<RadioSpeakerComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (ent.Comp.Enabled)
|
||||
EnsureComp<ActiveRadioComponent>(ent).Channels.UnionWith(ent.Comp.Channels);
|
||||
else
|
||||
RemCompDeferred<ActiveRadioComponent>(ent);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Toggling
|
||||
public void ToggleRadioMicrophone(EntityUid uid, EntityUid user, bool quiet = false, RadioMicrophoneComponent? component = null)
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnActivateMicrophone(Entity<RadioMicrophoneComponent> ent, ref ActivateInWorldEvent args)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!args.Complex)
|
||||
return;
|
||||
|
||||
SetMicrophoneEnabled(uid, user, !component.Enabled, quiet, component);
|
||||
if (!ent.Comp.ToggleOnInteract)
|
||||
return;
|
||||
|
||||
ToggleRadioMicrophone(ent.AsNullable(), args.User, args.Handled);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public virtual void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null) { }
|
||||
|
||||
public void ToggleRadioSpeaker(EntityUid uid, EntityUid user, bool quiet = false, RadioSpeakerComponent? component = null)
|
||||
[SubscribeLocalEvent]
|
||||
private void OnActivateSpeaker(Entity<RadioSpeakerComponent> ent, ref ActivateInWorldEvent args)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!args.Complex)
|
||||
return;
|
||||
|
||||
SetSpeakerEnabled(uid, user, !component.Enabled, quiet, component);
|
||||
if (!ent.Comp.ToggleOnInteract)
|
||||
return;
|
||||
|
||||
ToggleRadioSpeaker(ent.AsNullable(), args.User, args.Handled);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public void SetSpeakerEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioSpeakerComponent? component = null)
|
||||
[SubscribeLocalEvent]
|
||||
private void OnPowerChanged(Entity<RadioMicrophoneComponent> ent, ref PowerChangedEvent args)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (args.Powered)
|
||||
return;
|
||||
|
||||
component.Enabled = enabled;
|
||||
Dirty(uid, component);
|
||||
SetMicrophoneEnabled(ent.AsNullable(), null, false, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables a radio microphone.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity with the microphone.</param>
|
||||
/// <param name="user">The entity toggling the microphone, if any.</param>
|
||||
/// <param name="enabled">Whether the microphone should be enabled.</param>
|
||||
/// <param name="quiet">Whether to suppress the user-facing popup.</param>
|
||||
public void SetMicrophoneEnabled(
|
||||
Entity<RadioMicrophoneComponent?> ent,
|
||||
EntityUid? user,
|
||||
bool enabled,
|
||||
bool quiet = false
|
||||
)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
if (ent.Comp.PowerRequired && !_power.IsPowered(ent.Owner))
|
||||
return;
|
||||
|
||||
ent.Comp.Enabled = enabled;
|
||||
Dirty(ent);
|
||||
|
||||
if (!quiet && user != null)
|
||||
{
|
||||
var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state");
|
||||
var state = Loc.GetString(ent.Comp.Enabled
|
||||
? "handheld-radio-component-on-state"
|
||||
: "handheld-radio-component-off-state");
|
||||
var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
|
||||
_popup.PopupEntity(message, user.Value, user.Value);
|
||||
}
|
||||
|
||||
_appearance.SetData(uid, RadioDeviceVisuals.Speaker, component.Enabled);
|
||||
if (component.Enabled)
|
||||
EnsureComp<ActiveRadioComponent>(uid).Channels.UnionWith(component.Channels);
|
||||
_appearance.SetData(ent, RadioDeviceVisuals.Broadcasting, ent.Comp.Enabled);
|
||||
if (ent.Comp.Enabled)
|
||||
EnsureComp<ActiveListenerComponent>(ent).Range = ent.Comp.ListenRange;
|
||||
else
|
||||
RemCompDeferred<ActiveRadioComponent>(uid);
|
||||
RemCompDeferred<ActiveListenerComponent>(ent);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Toggling
|
||||
|
||||
/// <summary>
|
||||
/// Toggles a radio microphone.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity with the microphone.</param>
|
||||
/// <param name="user">The entity toggling the microphone.</param>
|
||||
/// <param name="quiet">Whether to suppress the user-facing popup.</param>
|
||||
public void ToggleRadioMicrophone(
|
||||
Entity<RadioMicrophoneComponent?> ent,
|
||||
EntityUid user,
|
||||
bool quiet = false
|
||||
)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
SetMicrophoneEnabled(ent, user, !ent.Comp.Enabled, quiet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles a radio speaker.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity with the speaker.</param>
|
||||
/// <param name="user">The entity toggling the speaker.</param>
|
||||
/// <param name="quiet">Whether to suppress the user-facing popup.</param>
|
||||
public void ToggleRadioSpeaker(
|
||||
Entity<RadioSpeakerComponent?> ent,
|
||||
EntityUid user,
|
||||
bool quiet = false
|
||||
)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
SetSpeakerEnabled(ent, user, !ent.Comp.Enabled, quiet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables a radio speaker.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity with the speaker.</param>
|
||||
/// <param name="user">The entity toggling the speaker, if any.</param>
|
||||
/// <param name="enabled">Whether the speaker should be enabled.</param>
|
||||
/// <param name="quiet">Whether to suppress the user-facing popup.</param>
|
||||
public void SetSpeakerEnabled(
|
||||
Entity<RadioSpeakerComponent?> ent,
|
||||
EntityUid? user,
|
||||
bool enabled,
|
||||
bool quiet = false
|
||||
)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
ent.Comp.Enabled = enabled;
|
||||
Dirty(ent);
|
||||
|
||||
if (!quiet && user != null)
|
||||
{
|
||||
var state = Loc.GetString(ent.Comp.Enabled
|
||||
? "handheld-radio-component-on-state"
|
||||
: "handheld-radio-component-off-state");
|
||||
var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
|
||||
_popup.PopupEntity(message, user.Value, user.Value);
|
||||
}
|
||||
|
||||
_appearance.SetData(ent, RadioDeviceVisuals.Speaker, ent.Comp.Enabled);
|
||||
if (ent.Comp.Enabled)
|
||||
EnsureComp<ActiveRadioComponent>(ent).Channels.UnionWith(ent.Comp.Channels);
|
||||
else
|
||||
RemCompDeferred<ActiveRadioComponent>(ent);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnExamine(Entity<RadioMicrophoneComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
var proto = ProtoMan.Index(ent.Comp.BroadcastChannel);
|
||||
|
||||
using (args.PushGroup(nameof(RadioMicrophoneComponent)))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("radio-microphone-component-examine",
|
||||
("color", proto.Color),
|
||||
("channel", proto.LocalizedName),
|
||||
("frequency", proto.Frequency)));
|
||||
}
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnListen(Entity<RadioMicrophoneComponent> ent, ref ListenEvent args)
|
||||
{
|
||||
if (HasComp<RadioSpeakerComponent>(args.Source))
|
||||
return; // no feedback loops please.
|
||||
|
||||
var channel = ProtoMan.Index(ent.Comp.BroadcastChannel);
|
||||
if (_recentlySent.Add((args.Message, args.Source, channel)))
|
||||
_radio.SendRadioMessage(args.Source, args.Message, channel, ent.Owner);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnAttemptListen(Entity<RadioMicrophoneComponent> ent, ref ListenAttemptEvent args)
|
||||
{
|
||||
if (ent.Comp.PowerRequired && !_power.IsPowered(ent.Owner)
|
||||
|| ent.Comp.UnobstructedRequired && !_interaction.InRangeUnobstructed(args.Source, ent.Owner, 0))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnReceiveRadio(Entity<RadioSpeakerComponent> ent, ref RadioReceiveEvent args)
|
||||
{
|
||||
if (ent.Owner == args.RadioSource)
|
||||
return;
|
||||
|
||||
var nameEv = new TransformSpeakerNameEvent(args.MessageSource, Name(args.MessageSource));
|
||||
RaiseLocalEvent(args.MessageSource, nameEv);
|
||||
|
||||
var name = Loc.GetString("speech-name-relay",
|
||||
("speaker", Name(ent.Owner)),
|
||||
("originalName", nameEv.VoiceName));
|
||||
|
||||
// log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
|
||||
_chat.TrySendInGameICMessage(ent.Owner,
|
||||
args.Message,
|
||||
InGameICChatType.Whisper,
|
||||
ChatTransmitRange.GhostRangeLimit,
|
||||
nameOverride: name,
|
||||
checkRadioPrefix: false);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnIntercomEncryptionChannelsChanged(
|
||||
Entity<IntercomComponent> ent,
|
||||
ref EncryptionChannelsChangedEvent args
|
||||
)
|
||||
{
|
||||
ent.Comp.SupportedChannels =
|
||||
args.Component.Channels.Select(p => new ProtoId<RadioChannelPrototype>(p)).ToList();
|
||||
|
||||
var channel = args.Component.DefaultChannel;
|
||||
if (ent.Comp.CurrentChannel != null && ent.Comp.SupportedChannels.Contains(ent.Comp.CurrentChannel.Value))
|
||||
channel = ent.Comp.CurrentChannel;
|
||||
|
||||
SetIntercomChannel(ent, channel);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnToggleIntercomMic(Entity<IntercomComponent> ent, ref ToggleIntercomMicMessage args)
|
||||
{
|
||||
if (ent.Comp.RequiresPower && !_power.IsPowered(ent.Owner))
|
||||
return;
|
||||
|
||||
SetMicrophoneEnabled(ent.Owner, args.Actor, args.Enabled, true);
|
||||
ent.Comp.MicrophoneEnabled = args.Enabled;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnToggleIntercomSpeaker(Entity<IntercomComponent> ent, ref ToggleIntercomSpeakerMessage args)
|
||||
{
|
||||
if (ent.Comp.RequiresPower && !_power.IsPowered(ent.Owner))
|
||||
return;
|
||||
|
||||
SetSpeakerEnabled(ent.Owner, args.Actor, args.Enabled, true);
|
||||
ent.Comp.SpeakerEnabled = args.Enabled;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnSelectIntercomChannel(Entity<IntercomComponent> ent, ref SelectIntercomChannelMessage args)
|
||||
{
|
||||
if (ent.Comp.RequiresPower && !_power.IsPowered(ent.Owner))
|
||||
return;
|
||||
|
||||
if (!ProtoMan.HasIndex<RadioChannelPrototype>(args.Channel) ||
|
||||
!ent.Comp.SupportedChannels.Contains(args.Channel))
|
||||
return;
|
||||
|
||||
SetIntercomChannel(ent, args.Channel);
|
||||
}
|
||||
|
||||
private void SetIntercomChannel(Entity<IntercomComponent> ent, ProtoId<RadioChannelPrototype>? channel)
|
||||
{
|
||||
ent.Comp.CurrentChannel = channel;
|
||||
|
||||
if (TryComp<RadioMicrophoneComponent>(ent, out var mic))
|
||||
{
|
||||
if (channel == null)
|
||||
{
|
||||
SetMicrophoneEnabled(ent.Owner, null, false);
|
||||
ent.Comp.MicrophoneEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mic.BroadcastChannel = channel.Value;
|
||||
Dirty(ent, mic);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<RadioSpeakerComponent>(ent, out var speaker))
|
||||
{
|
||||
if (channel == null)
|
||||
{
|
||||
SetSpeakerEnabled(ent.Owner, null, false);
|
||||
ent.Comp.SpeakerEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
speaker.Channels = [channel.Value];
|
||||
Dirty(ent, speaker);
|
||||
}
|
||||
}
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
41
Content.Shared/Radio/EntitySystems/SharedRadioSystem.cs
Normal file
41
Content.Shared/Radio/EntitySystems/SharedRadioSystem.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Radio.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles intrinsic radios and the general process of converting radio messages into chat messages.
|
||||
/// </summary>
|
||||
public abstract partial class SharedRadioSystem : EntitySystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Send radio message to all active radio listeners.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void SendRadioMessage(EntityUid messageSource,
|
||||
string message,
|
||||
ProtoId<RadioChannelPrototype> channel,
|
||||
EntityUid radioSource,
|
||||
bool escapeMarkup = true)
|
||||
{
|
||||
SendRadioMessage(messageSource, message, ProtoMan.Index(channel), radioSource, escapeMarkup: escapeMarkup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a radio message to all active radio listeners.
|
||||
/// </summary>
|
||||
/// <param name="messageSource">Entity that spoke the message.</param>
|
||||
/// <param name="message">Message to send over the radio.</param>
|
||||
/// <param name="channel">Radio channel to send the message on.</param>
|
||||
/// <param name="radioSource">Entity transmitting the message.</param>
|
||||
/// <param name="escapeMarkup">Whether markup in the message should be escaped.</param>
|
||||
[PublicAPI]
|
||||
public virtual void SendRadioMessage(EntityUid messageSource,
|
||||
string message,
|
||||
RadioChannelPrototype channel,
|
||||
EntityUid radioSource,
|
||||
bool escapeMarkup = true)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,21 @@
|
|||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Radio;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a radio channel and its transmission properties.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class RadioChannelPrototype : IPrototype
|
||||
{
|
||||
[IdDataField, ViewVariables]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name for the channel.
|
||||
/// </summary>
|
||||
[DataField("name")]
|
||||
[DataField]
|
||||
public LocId Name { get; private set; } = string.Empty;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
|
|
@ -20,19 +27,21 @@ public sealed partial class RadioChannelPrototype : IPrototype
|
|||
[DataField("keycode")]
|
||||
public char KeyCode { get; private set; } = '\0';
|
||||
|
||||
[DataField("frequency")]
|
||||
public int Frequency { get; private set; } = 0;
|
||||
|
||||
[DataField("color")]
|
||||
public Color Color { get; private set; } = Color.Lime;
|
||||
|
||||
[IdDataField, ViewVariables]
|
||||
public string ID { get; private set; } = default!;
|
||||
/// <summary>
|
||||
/// Frequency used by the channel.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public FixedPoint2 Frequency { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// If channel is long range it doesn't require telecommunication server
|
||||
/// and messages can be sent across different stations
|
||||
/// Color used to display the channel.
|
||||
/// </summary>
|
||||
[DataField("longRange"), ViewVariables]
|
||||
public bool LongRange = false;
|
||||
[DataField]
|
||||
public Color Color { get; private set; } = Color.Lime;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the channel can transmit across different stations without a telecommunications server.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool LongRange;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
using Content.Shared.Chat;
|
||||
using Content.Shared.Radio;
|
||||
|
||||
namespace Content.Server.Radio;
|
||||
namespace Content.Shared.Radio;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a radio message is received.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct RadioReceiveEvent(string Message, EntityUid MessageSource, RadioChannelPrototype Channel, EntityUid RadioSource, MsgChatMessage ChatMsg);
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on the parent entity of a headset radio when a radio message is received
|
||||
/// Event raised on the parent entity of a headset radio when a radio message is received.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct HeadsetRadioReceiveRelayEvent(RadioReceiveEvent RelayedEvent);
|
||||
|
||||
/// <summary>
|
||||
/// Use this event to cancel sending message per receiver
|
||||
/// Use this event to cancel sending message per receiver.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct RadioReceiveAttemptEvent(RadioChannelPrototype Channel, EntityUid RadioSource, EntityUid RadioReceiver)
|
||||
|
|
@ -25,7 +27,7 @@ public record struct RadioReceiveAttemptEvent(RadioChannelPrototype Channel, Ent
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this event to cancel sending message to every receiver
|
||||
/// Use this event to cancel sending message to every receiver.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct RadioSendAttemptEvent(RadioChannelPrototype Channel, EntityUid RadioSource)
|
||||
|
|
@ -6,5 +6,5 @@ encryption-keys-are-locked = Encryption key slots are locked!
|
|||
encryption-keys-panel-locked = Open maintenance panel first!
|
||||
|
||||
examine-encryption-channels-prefix = Available frequencies:
|
||||
examine-encryption-channel = [color={$color}]{$key} for {$id} ({NATURALFIXED($freq, 1)})[/color]
|
||||
examine-encryption-channel = [color={$color}]{$key} for {$id} ({$freq})[/color]
|
||||
examine-encryption-default-channel = The default channel is [color={$color}]{$channel}[/color].
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
handheld-radio-component-on-use = The radio is now {$radioState}.
|
||||
handheld-radio-component-on-examine = It's set to broadcast over the {$frequency} frequency.
|
||||
handheld-radio-component-on-state = on
|
||||
handheld-radio-component-off-state = off
|
||||
handheld-radio-component-channel-set = Channel set to {$channel}
|
||||
handheld-radio-component-chennel-examine = The current channel is {$channel}.
|
||||
|
|
@ -0,0 +1 @@
|
|||
radio-microphone-component-examine = The current channel is [color={$color}]{$channel} ({$frequency})[/color].
|
||||
|
|
@ -2,14 +2,14 @@
|
|||
id: Common
|
||||
name: chat-radio-common
|
||||
keycode: ";"
|
||||
frequency: 1459
|
||||
frequency: 145.9
|
||||
color: "#2cdb2c"
|
||||
|
||||
- type: radioChannel
|
||||
id: CentCom
|
||||
name: chat-radio-centcom
|
||||
keycode: 'y'
|
||||
frequency: 1337
|
||||
frequency: 133.7
|
||||
color: "#2681a5"
|
||||
longRange: true
|
||||
|
||||
|
|
@ -17,63 +17,63 @@
|
|||
id: Command
|
||||
name: chat-radio-command
|
||||
keycode: 'c'
|
||||
frequency: 1353
|
||||
frequency: 135.3
|
||||
color: "#fcdf03"
|
||||
|
||||
- type: radioChannel
|
||||
id: Engineering
|
||||
name: chat-radio-engineering
|
||||
keycode: 'e'
|
||||
frequency: 1357
|
||||
frequency: 135.7
|
||||
color: "#ff733c"
|
||||
|
||||
- type: radioChannel
|
||||
id: Medical
|
||||
name: chat-radio-medical
|
||||
keycode: 'm'
|
||||
frequency: 1355
|
||||
frequency: 135.5
|
||||
color: "#57b8f0"
|
||||
|
||||
- type: radioChannel
|
||||
id: Science
|
||||
name: chat-radio-science
|
||||
keycode: 'n'
|
||||
frequency: 1351
|
||||
frequency: 135.1
|
||||
color: "#cd7ccd"
|
||||
|
||||
- type: radioChannel
|
||||
id: Security
|
||||
name: chat-radio-security
|
||||
keycode: 's'
|
||||
frequency: 1359
|
||||
frequency: 135.9
|
||||
color: "#ff4242"
|
||||
|
||||
- type: radioChannel
|
||||
id: Service
|
||||
name: chat-radio-service
|
||||
keycode: 'v'
|
||||
frequency: 1349
|
||||
frequency: 134.9
|
||||
color: "#539c00"
|
||||
|
||||
- type: radioChannel
|
||||
id: Supply
|
||||
name: chat-radio-supply
|
||||
keycode: 'u'
|
||||
frequency: 1347
|
||||
frequency: 134.7
|
||||
color: "#b48b57"
|
||||
|
||||
- type: radioChannel
|
||||
id: Syndicate
|
||||
name: chat-radio-syndicate
|
||||
keycode: 't'
|
||||
frequency: 1213
|
||||
frequency: 121.3
|
||||
color: "#8f4a4b"
|
||||
longRange: true
|
||||
|
||||
- type: radioChannel
|
||||
id: Handheld
|
||||
name: chat-radio-handheld
|
||||
frequency: 1330
|
||||
frequency: 133.0
|
||||
color: "#967101"
|
||||
# long range since otherwise it'd defeat the point of a handheld radio independent of telecomms
|
||||
longRange: true
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
id: Binary
|
||||
name: chat-radio-binary
|
||||
keycode: 'b'
|
||||
frequency: 1001
|
||||
frequency: 100.1
|
||||
color: "#5ed7aa"
|
||||
# long range since otherwise it'd defeat the point of a handheld radio independent of telecomms
|
||||
longRange: true
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
id: Freelance
|
||||
name: chat-radio-freelance
|
||||
keycode: 'f'
|
||||
frequency: 1984
|
||||
frequency: 198.4
|
||||
color: "#f6ce64"
|
||||
# long range since otherwise it'd defeat the point of a handheld radio independent of telecomms
|
||||
longRange: true
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
id: Xenoborg
|
||||
name: chat-radio-xenoborg
|
||||
keycode: 'x'
|
||||
frequency: 2002
|
||||
frequency: 200.2
|
||||
color: "#2288ff"
|
||||
# long range since I don't wanna make a special xenoborg telecomm server
|
||||
longRange: true
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
id: Mothership
|
||||
name: chat-radio-mothership
|
||||
keycode: 'z'
|
||||
frequency: 2003
|
||||
frequency: 200.3
|
||||
color: "#ff2222"
|
||||
# long range since I don't wanna make a special xenoborg telecomm server
|
||||
longRange: true
|
||||
longRange: true
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue