Update radio prefix parsing (#13777)
This commit is contained in:
@@ -1,5 +1,133 @@
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Radio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Chat;
|
||||
|
||||
public abstract class SharedChatSystem : EntitySystem
|
||||
{
|
||||
public const char RadioCommonPrefix = ';';
|
||||
public const char RadioChannelPrefix = ':';
|
||||
public const char LocalPrefix = '.';
|
||||
public const char ConsolePrefix = '/';
|
||||
public const char DeadPrefix = '\\';
|
||||
public const char LOOCPrefix = '(';
|
||||
public const char OOCPrefix = '[';
|
||||
public const char EmotesPrefix = '@';
|
||||
public const char AdminPrefix = ']';
|
||||
public const char WhisperPrefix = ',';
|
||||
|
||||
public const char DefaultChannelKey = 'h';
|
||||
public const string CommonChannel = "Common";
|
||||
public static string DefaultChannelPrefix = $"{RadioChannelPrefix}{DefaultChannelKey}";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Cache of the keycodes for faster lookup.
|
||||
/// </summary>
|
||||
private Dictionary<char, RadioChannelPrototype> _keyCodes = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DebugTools.Assert(_prototypeManager.HasIndex<RadioChannelPrototype>(CommonChannel));
|
||||
_prototypeManager.PrototypesReloaded += OnPrototypeReload;
|
||||
CacheRadios();
|
||||
}
|
||||
|
||||
private void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (obj.ByType.ContainsKey(typeof(RadioChannelPrototype)))
|
||||
CacheRadios();
|
||||
}
|
||||
|
||||
private void CacheRadios()
|
||||
{
|
||||
_keyCodes.Clear();
|
||||
|
||||
foreach (var proto in _prototypeManager.EnumeratePrototypes<RadioChannelPrototype>())
|
||||
{
|
||||
_keyCodes.Add(proto.KeyCode, proto);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_prototypeManager.PrototypesReloaded -= OnPrototypeReload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to resolve radio prefixes in chat messages (e.g., remove a leading ":e" and resolve the requested
|
||||
/// channel. Returns true if a radio message was attempted, even if the channel is invalid.
|
||||
/// </summary>
|
||||
/// <param name="source">Source of the message</param>
|
||||
/// <param name="input">The message to be modified</param>
|
||||
/// <param name="output">The modified message</param>
|
||||
/// <param name="channel">The channel that was requested, if any</param>
|
||||
/// <param name="quiet">Whether or not to generate an informative pop-up message.</param>
|
||||
/// <returns></returns>
|
||||
public bool TryProccessRadioMessage(
|
||||
EntityUid source,
|
||||
string input,
|
||||
out string output,
|
||||
out RadioChannelPrototype? channel,
|
||||
bool quiet = false)
|
||||
{
|
||||
output = input.Trim();
|
||||
channel = null;
|
||||
|
||||
if (input.Length == 0)
|
||||
return false;
|
||||
|
||||
if (input.StartsWith(RadioCommonPrefix))
|
||||
{
|
||||
output = SanitizeMessageCapital(input[1..].TrimStart());
|
||||
channel = _prototypeManager.Index<RadioChannelPrototype>(CommonChannel);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!input.StartsWith(RadioChannelPrefix))
|
||||
return false;
|
||||
|
||||
if (input.Length < 2 || char.IsWhiteSpace(input[1]))
|
||||
{
|
||||
output = SanitizeMessageCapital(input[1..].TrimStart());
|
||||
if (!quiet)
|
||||
_popup.PopupEntity(Loc.GetString("chat-manager-no-radio-key"), source, source);
|
||||
return true;
|
||||
}
|
||||
|
||||
var channelKey = input[1];
|
||||
output = SanitizeMessageCapital(input[2..].TrimStart());
|
||||
|
||||
if (channelKey == DefaultChannelKey)
|
||||
{
|
||||
var ev = new GetDefaultRadioChannelEvent();
|
||||
RaiseLocalEvent(source, ev);
|
||||
|
||||
if (ev.Channel != null)
|
||||
_prototypeManager.TryIndex(ev.Channel, out channel);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_keyCodes.TryGetValue(channelKey, out channel) && !quiet)
|
||||
{
|
||||
var msg = Loc.GetString("chat-manager-no-such-channel", ("key", channelKey));
|
||||
_popup.PopupEntity(msg, source, source);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string SanitizeMessageCapital(string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return message;
|
||||
// Capitalize first letter
|
||||
message = char.ToUpper(message[0]) + message.Remove(0, 1);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Electrocution;
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.Strip.Components;
|
||||
using Content.Shared.Temperature;
|
||||
@@ -21,6 +22,7 @@ public partial class InventorySystem
|
||||
SubscribeLocalEvent<InventoryComponent, BeforeStripEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
|
||||
}
|
||||
|
||||
protected void RelayInventoryEvent<T>(EntityUid uid, InventoryComponent component, T args) where T : EntityEventArgs, IInventoryRelayEvent
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||
|
||||
namespace Content.Server.Radio.Components;
|
||||
namespace Content.Shared.Radio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This component is currently used for providing access to channels for "HeadsetComponent"s.
|
||||
/// It should be used for intercoms and other radios in future.
|
||||
@@ -13,10 +14,8 @@ public sealed class EncryptionKeyComponent : Component
|
||||
[DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
|
||||
public HashSet<string> Channels = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This variable defines what channel will be used with using ":h" (department channel prefix).
|
||||
/// Headset read DefaultChannel of first encryption key installed.
|
||||
/// This is the channel that will be used when using the default/department prefix (<see cref="SharedChatSystem.DefaultChannelKey"/>).
|
||||
/// </summary>
|
||||
[DataField("defaultChannel", customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
|
||||
public readonly string? DefaultChannel;
|
||||
@@ -0,0 +1,56 @@
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Tools;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Radio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This component is by entities that can contain encryption keys
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class EncryptionKeyHolderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not encryption keys can be removed from the headset.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("keysUnlocked")]
|
||||
public bool KeysUnlocked = true;
|
||||
|
||||
/// <summary>
|
||||
/// The tool required to extract the encryption keys from the headset.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("keysExtractionMethod", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
||||
public string KeysExtractionMethod = "Screwing";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("keySlots")]
|
||||
public int KeySlots = 2;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("keyExtractionSound")]
|
||||
public SoundSpecifier KeyExtractionSound = new SoundPathSpecifier("/Audio/Items/pistol_magout.ogg");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("keyInsertionSound")]
|
||||
public SoundSpecifier KeyInsertionSound = new SoundPathSpecifier("/Audio/Items/pistol_magin.ogg");
|
||||
|
||||
[ViewVariables]
|
||||
public Container KeyContainer = default!;
|
||||
public const string KeyContainerName = "key_slots";
|
||||
|
||||
/// <summary>
|
||||
/// Combined set of radio channels provided by all contained keys.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public HashSet<string> Channels = new();
|
||||
|
||||
/// <summary>
|
||||
/// This is the channel that will be used when using the default/department prefix (<see cref="SharedChatSystem.DefaultChannelKey"/>).
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string? DefaultChannel;
|
||||
}
|
||||
18
Content.Shared/Radio/Components/HeadsetComponent.cs
Normal file
18
Content.Shared/Radio/Components/HeadsetComponent.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Shared.Inventory;
|
||||
|
||||
namespace Content.Shared.Radio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This component relays radio messages to the parent entity's chat when equipped.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class HeadsetComponent : Component
|
||||
{
|
||||
[DataField("enabled")]
|
||||
public bool Enabled = true;
|
||||
|
||||
public bool IsEquipped = false;
|
||||
|
||||
[DataField("requiredSlot")]
|
||||
public SlotFlags RequiredSlot = SlotFlags.EARS;
|
||||
}
|
||||
13
Content.Shared/Radio/EncryptionChannelsChangedEvent.cs
Normal file
13
Content.Shared/Radio/EncryptionChannelsChangedEvent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Content.Shared.Radio.Components;
|
||||
|
||||
namespace Content.Shared.Radio;
|
||||
|
||||
public sealed class EncryptionChannelsChangedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EncryptionKeyHolderComponent Component;
|
||||
|
||||
public EncryptionChannelsChangedEvent(EncryptionKeyHolderComponent component)
|
||||
{
|
||||
Component = component;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using Content.Server.Radio.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Radio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Radio.EntitySystems;
|
||||
|
||||
public sealed class EncryptionKeySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EncryptionKeyComponent, ExaminedEvent>(OnExamined);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, EncryptionKeyComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
if(component.Channels.Count > 0)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("examine-encryption-key-channels-prefix"));
|
||||
EncryptionKeySystem.GetChannelsExamine(component.Channels, args, _protoManager, "examine-headset-channel");
|
||||
if (component.DefaultChannel != null)
|
||||
{
|
||||
var proto = _protoManager.Index<RadioChannelPrototype>(component.DefaultChannel);
|
||||
args.PushMarkup(Loc.GetString("examine-encryption-key-default-channel", ("channel", component.DefaultChannel), ("color", proto.Color)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A static method for formating list of radio channels for examine events.
|
||||
/// </summary>
|
||||
/// <param name="channels">HashSet of channels in headset, encryptionkey or etc.</param>
|
||||
/// <param name="protoManager">IPrototypeManager for getting prototypes of channels with their variables.</param>
|
||||
/// <param name="channelFTLPattern">String that provide id of pattern in .ftl files to format channel with variables of it.</param>
|
||||
public static void GetChannelsExamine(HashSet<string> channels, ExaminedEvent examineEvent, IPrototypeManager protoManager, string channelFTLPattern)
|
||||
{
|
||||
foreach (var id in channels)
|
||||
{
|
||||
var proto = protoManager.Index<RadioChannelPrototype>(id);
|
||||
string keyCode = "" + proto.KeyCode;
|
||||
if (id != "Common")
|
||||
keyCode = ":" + keyCode;
|
||||
examineEvent.PushMarkup(Loc.GetString(channelFTLPattern,
|
||||
("color", proto.Color),
|
||||
("key", keyCode),
|
||||
("id", proto.LocalizedName),
|
||||
("freq", proto.Frequency)));
|
||||
}
|
||||
}
|
||||
}
|
||||
196
Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
Normal file
196
Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Radio.Components;
|
||||
using Content.Shared.Tools;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Radio.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This system manages encryption keys & key holders for use with radio channels.
|
||||
/// </summary>
|
||||
public sealed class EncryptionKeySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedToolSystem _toolSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<EncryptionKeyComponent, ExaminedEvent>(OnKeyExamined);
|
||||
SubscribeLocalEvent<EncryptionKeyHolderComponent, ExaminedEvent>(OnHolderExamined);
|
||||
|
||||
SubscribeLocalEvent<EncryptionKeyHolderComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<EncryptionKeyHolderComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<EncryptionKeyHolderComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<EncryptionKeyHolderComponent, EntRemovedFromContainerMessage>(OnContainerModified);
|
||||
}
|
||||
|
||||
public void UpdateChannels(EntityUid uid, EncryptionKeyHolderComponent component)
|
||||
{
|
||||
if (!component.Initialized)
|
||||
return;
|
||||
|
||||
component.Channels.Clear();
|
||||
component.DefaultChannel = null;
|
||||
|
||||
foreach (var ent in component.KeyContainer.ContainedEntities)
|
||||
{
|
||||
if (TryComp<EncryptionKeyComponent>(ent, out var key))
|
||||
{
|
||||
component.Channels.UnionWith(key.Channels);
|
||||
component.DefaultChannel ??= key.DefaultChannel;
|
||||
}
|
||||
}
|
||||
|
||||
RaiseLocalEvent(uid, new EncryptionChannelsChangedEvent(component));
|
||||
}
|
||||
|
||||
private void OnContainerModified(EntityUid uid, EncryptionKeyHolderComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
if (args.Container.ID == EncryptionKeyHolderComponent.KeyContainerName)
|
||||
UpdateChannels(uid, component);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, EncryptionKeyHolderComponent component, InteractUsingEvent args)
|
||||
{
|
||||
if (!TryComp<ContainerManagerComponent>(uid, out var storage))
|
||||
return;
|
||||
|
||||
if (TryComp<EncryptionKeyComponent>(args.Used, out var key))
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
if (!component.KeysUnlocked)
|
||||
{
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
_popupSystem.PopupEntity(Loc.GetString("headset-encryption-keys-are-locked"), uid, Filter.Local(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.KeySlots <= component.KeyContainer.ContainedEntities.Count)
|
||||
{
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
_popupSystem.PopupEntity(Loc.GetString("headset-encryption-key-slots-already-full"), uid, Filter.Local(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.KeyContainer.Insert(args.Used))
|
||||
{
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
_popupSystem.PopupEntity(Loc.GetString("headset-encryption-key-successfully-installed"), uid, Filter.Local(), false);
|
||||
_audio.PlayPredicted(component.KeyInsertionSound, args.Target, args.User);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryComp<ToolComponent>(args.Used, out var tool) || !tool.Qualities.Contains(component.KeysExtractionMethod))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
if (component.KeyContainer.ContainedEntities.Count == 0)
|
||||
{
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
_popupSystem.PopupEntity(Loc.GetString("headset-encryption-keys-no-keys"), uid, Filter.Local(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_toolSystem.UseTool(args.Used, args.User, uid, 0f, 0f, component.KeysExtractionMethod, toolComponent: tool))
|
||||
return;
|
||||
|
||||
var contained = component.KeyContainer.ContainedEntities.ToArray();
|
||||
_container.EmptyContainer(component.KeyContainer, entMan: EntityManager);
|
||||
foreach (var ent in contained)
|
||||
{
|
||||
_hands.PickupOrDrop(args.User, ent);
|
||||
}
|
||||
|
||||
// if tool use ever gets predicted this needs changing.
|
||||
_popupSystem.PopupEntity(Loc.GetString("headset-encryption-keys-all-extracted"), uid, args.User);
|
||||
_audio.PlayPvs(component.KeyExtractionSound, args.Target);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, EncryptionKeyHolderComponent component, ComponentStartup args)
|
||||
{
|
||||
component.KeyContainer = _container.EnsureContainer<Container>(uid, EncryptionKeyHolderComponent.KeyContainerName);
|
||||
UpdateChannels(uid, component);
|
||||
}
|
||||
|
||||
private void OnHolderExamined(EntityUid uid, EncryptionKeyHolderComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
if (component.KeyContainer.ContainedEntities.Count == 0)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("examine-headset-no-keys"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Channels.Count > 0)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("examine-headset-channels-prefix"));
|
||||
AddChannelsExamine(component.Channels, component.DefaultChannel, args, _protoManager, "examine-headset-channel");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyExamined(EntityUid uid, EncryptionKeyComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
if(component.Channels.Count > 0)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("examine-encryption-key-channels-prefix"));
|
||||
AddChannelsExamine(component.Channels, component.DefaultChannel, args, _protoManager, "examine-headset-channel");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method for formating list of radio channels for examine events.
|
||||
/// </summary>
|
||||
/// <param name="channels">HashSet of channels in headset, encryptionkey or etc.</param>
|
||||
/// <param name="protoManager">IPrototypeManager for getting prototypes of channels with their variables.</param>
|
||||
/// <param name="channelFTLPattern">String that provide id of pattern in .ftl files to format channel with variables of it.</param>
|
||||
public void AddChannelsExamine(HashSet<string> channels, string? defaultChannel, ExaminedEvent examineEvent, IPrototypeManager protoManager, string channelFTLPattern)
|
||||
{
|
||||
RadioChannelPrototype? proto;
|
||||
foreach (var id in channels)
|
||||
{
|
||||
proto = protoManager.Index<RadioChannelPrototype>(id);
|
||||
|
||||
var key = id == SharedChatSystem.CommonChannel
|
||||
? SharedChatSystem.RadioCommonPrefix.ToString()
|
||||
: $"{SharedChatSystem.RadioChannelPrefix}{proto.KeyCode}";
|
||||
|
||||
examineEvent.PushMarkup(Loc.GetString(channelFTLPattern,
|
||||
("color", proto.Color),
|
||||
("key", key),
|
||||
("id", proto.LocalizedName),
|
||||
("freq", proto.Frequency)));
|
||||
}
|
||||
|
||||
if (defaultChannel != null && _protoManager.TryIndex(defaultChannel, out proto))
|
||||
{
|
||||
var msg = Loc.GetString("examine-default-channel",
|
||||
("prefix", SharedChatSystem.DefaultChannelPrefix),
|
||||
("channel", defaultChannel),
|
||||
("color", proto.Color));
|
||||
examineEvent.PushMarkup(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Content.Shared/Radio/EntitySystems/SharedHeadsetSystem.cs
Normal file
38
Content.Shared/Radio/EntitySystems/SharedHeadsetSystem.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Radio.Components;
|
||||
|
||||
namespace Content.Shared.Radio.EntitySystems;
|
||||
|
||||
public abstract class SharedHeadsetSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<HeadsetComponent, InventoryRelayedEvent<GetDefaultRadioChannelEvent>>(OnGetDefault);
|
||||
SubscribeLocalEvent<HeadsetComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<HeadsetComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
}
|
||||
|
||||
private void OnGetDefault(EntityUid uid, HeadsetComponent component, InventoryRelayedEvent<GetDefaultRadioChannelEvent> args)
|
||||
{
|
||||
if (!component.Enabled || !component.IsEquipped)
|
||||
{
|
||||
// don't provide default channels from pocket slots.
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp(uid, out EncryptionKeyHolderComponent? keyHolder))
|
||||
args.Args.Channel ??= keyHolder.DefaultChannel;
|
||||
}
|
||||
|
||||
protected virtual void OnGotEquipped(EntityUid uid, HeadsetComponent component, GotEquippedEvent args)
|
||||
{
|
||||
component.IsEquipped = args.SlotFlags.HasFlag(component.RequiredSlot);
|
||||
}
|
||||
|
||||
protected virtual void OnGotUnequipped(EntityUid uid, HeadsetComponent component, GotUnequippedEvent args)
|
||||
{
|
||||
component.IsEquipped = false;
|
||||
}
|
||||
}
|
||||
15
Content.Shared/Radio/GetDefaultRadioChannelEvent.cs
Normal file
15
Content.Shared/Radio/GetDefaultRadioChannelEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Inventory;
|
||||
|
||||
namespace Content.Shared.Radio;
|
||||
|
||||
public sealed class GetDefaultRadioChannelEvent : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Id of the default <see cref="RadioChannelPrototype"/> that will get addressed when using the
|
||||
/// department/default channel prefix. See <see cref="SharedChatSystem.DefaultChannelKey"/>.
|
||||
/// </summary>
|
||||
public string? Channel;
|
||||
|
||||
public SlotFlags TargetSlots => ~SlotFlags.POCKET;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -19,6 +20,32 @@ public abstract class SharedToolSystem : EntitySystem
|
||||
SubscribeLocalEvent<MultipleToolComponent, ComponentHandleState>(OnMultipleToolHandleState);
|
||||
}
|
||||
|
||||
public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel,
|
||||
float doAfterDelay, string toolQualityNeeded, object? doAfterCompleteEvent = null, object? doAfterCancelledEvent = null, EntityUid? doAfterEventTarget = null,
|
||||
Func<bool>? doAfterCheck = null, ToolComponent? toolComponent = null)
|
||||
{
|
||||
return UseTool(tool, user, target, fuel, doAfterDelay, new[] { toolQualityNeeded },
|
||||
doAfterCompleteEvent, doAfterCancelledEvent, doAfterEventTarget, doAfterCheck, toolComponent);
|
||||
}
|
||||
|
||||
public virtual bool UseTool(
|
||||
EntityUid tool,
|
||||
EntityUid user,
|
||||
EntityUid? target,
|
||||
float fuel,
|
||||
float doAfterDelay,
|
||||
IEnumerable<string> toolQualitiesNeeded,
|
||||
object? doAfterCompleteEvent = null,
|
||||
object? doAfterCancelledEvent = null,
|
||||
EntityUid? doAfterEventTarget = null,
|
||||
Func<bool>? doAfterCheck = null,
|
||||
ToolComponent? toolComponent = null,
|
||||
CancellationToken? cancelToken = null)
|
||||
{
|
||||
// predicted tools when.
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnMultipleToolHandleState(EntityUid uid, MultipleToolComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not MultipleToolComponentState state)
|
||||
|
||||
Reference in New Issue
Block a user