Add department-specific radio channels (#9061)
* Add department-specific radio channels
This commit adds working department-specific radio channels, while
minimizing damage to the current codebase. It is expected that a future
refactor will clean this up a bit.
ChatSystem now has a RadioPrefix() method that recognizes
department-specific channels (e.g. ":e" and ":m") in addition to the
global channel (";"). It strips the prefix from the message and assigns
messages an integer representing the destination channel, if any.
IListen and IRadio now accept optional 'channel' arguments with this
channel in mind.
The ugly is that the integer channel number is hard-coded and also shows
up in chat.
Comms are not modeled at this time. You cannot break comms (yet).
All headsets have channels soldered into them. You cannot change
encryption keys to hop on new channels. Steal a headset instead.
* Remove debugging print
* Convert to prototypes
* Use prototype names in headset prototype
* Adjust list style
* Document prototype fields
* cringe
* some cleanup
* colours
* Remove alphas at least
* cc
Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Administration;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
88
Content.Server/Chat/Systems/ChatSystem.Radio.cs
Normal file
88
Content.Server/Chat/Systems/ChatSystem.Radio.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Headset;
|
||||
using Content.Shared.Radio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Chat.Systems;
|
||||
|
||||
public sealed partial class ChatSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Cache of the keycodes for faster lookup.
|
||||
/// </summary>
|
||||
private Dictionary<char, RadioChannelPrototype> _keyCodes = new();
|
||||
|
||||
private void InitializeRadio()
|
||||
{
|
||||
_prototypeManager.PrototypesReloaded += OnPrototypeReload;
|
||||
CacheRadios();
|
||||
}
|
||||
|
||||
private void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
CacheRadios();
|
||||
}
|
||||
|
||||
private void CacheRadios()
|
||||
{
|
||||
_keyCodes.Clear();
|
||||
|
||||
foreach (var proto in _prototypeManager.EnumeratePrototypes<RadioChannelPrototype>())
|
||||
{
|
||||
_keyCodes.Add(proto.KeyCode, proto);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShutdownRadio()
|
||||
{
|
||||
_prototypeManager.PrototypesReloaded -= OnPrototypeReload;
|
||||
}
|
||||
|
||||
private (string, RadioChannelPrototype?) GetRadioPrefix(EntityUid source, string message)
|
||||
{
|
||||
// TODO: Turn common into a true frequency and support multiple aliases.
|
||||
var channelMessage = message.StartsWith(':') || message.StartsWith('.');
|
||||
var radioMessage = message.StartsWith(';') || channelMessage;
|
||||
if (!radioMessage) return (message, null);
|
||||
|
||||
// Special case for empty messages
|
||||
if (message.Length <= 1)
|
||||
return (string.Empty, null);
|
||||
|
||||
// Look for a prefix indicating a destination radio channel.
|
||||
RadioChannelPrototype? chan;
|
||||
if (channelMessage && message.Length >= 2)
|
||||
{
|
||||
_keyCodes.TryGetValue(message[1], out chan);
|
||||
|
||||
if (chan == null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("chat-manager-no-such-channel"), source, Filter.Entities(source));
|
||||
chan = null;
|
||||
}
|
||||
|
||||
// Strip message prefix.
|
||||
message = message[2..].TrimStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove semicolon
|
||||
message = message[1..].TrimStart();
|
||||
chan = _prototypeManager.Index<RadioChannelPrototype>("Common");
|
||||
}
|
||||
|
||||
if (_inventory.TryGetSlotEntity(source, "ears", out var entityUid) &&
|
||||
TryComp(entityUid, out HeadsetComponent? headset))
|
||||
{
|
||||
headset.RadioRequested = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("chat-manager-no-headset-on-message"), source, Filter.Entities(source));
|
||||
}
|
||||
|
||||
return (message, chan);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Ghost.Components;
|
||||
using Content.Server.Headset;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Radio.EntitySystems;
|
||||
@@ -23,22 +22,24 @@ using Robust.Shared.Console;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Chat;
|
||||
namespace Content.Server.Chat.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// ChatSystem is responsible for in-simulation chat handling, such as whispering, speaking, emoting, etc.
|
||||
/// ChatSystem depends on ChatManager to actually send the messages.
|
||||
/// </summary>
|
||||
public sealed class ChatSystem : SharedChatSystem
|
||||
public sealed partial class ChatSystem : SharedChatSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IChatSanitizationManager _sanitizer = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
@@ -56,6 +57,7 @@ public sealed class ChatSystem : SharedChatSystem
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeRadio();
|
||||
_configurationManager.OnValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged, true);
|
||||
|
||||
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnGameChange);
|
||||
@@ -63,6 +65,7 @@ public sealed class ChatSystem : SharedChatSystem
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
ShutdownRadio();
|
||||
_configurationManager.UnsubValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged);
|
||||
}
|
||||
|
||||
@@ -226,14 +229,18 @@ public sealed class ChatSystem : SharedChatSystem
|
||||
if (!_actionBlocker.CanSpeak(source)) return;
|
||||
message = TransformSpeech(source, message);
|
||||
|
||||
_listener.PingListeners(source, message);
|
||||
(message, var channel) = GetRadioPrefix(source, message);
|
||||
|
||||
if (channel != null)
|
||||
_listener.PingListeners(source, message, channel);
|
||||
|
||||
var messageWrap = Loc.GetString("chat-manager-entity-say-wrap-message",
|
||||
("entityName", Name(source)));
|
||||
|
||||
SendInVoiceRange(ChatChannel.Local, message, messageWrap, source, hideChat);
|
||||
|
||||
var ev = new EntitySpokeEvent(message);
|
||||
RaiseLocalEvent(source, ev, false);
|
||||
RaiseLocalEvent(source, ev);
|
||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Say from {ToPrettyString(source):user}: {message}");
|
||||
}
|
||||
|
||||
@@ -242,7 +249,6 @@ public sealed class ChatSystem : SharedChatSystem
|
||||
if (!_actionBlocker.CanSpeak(source)) return;
|
||||
|
||||
message = TransformSpeech(source, message);
|
||||
_listener.PingListeners(source, message);
|
||||
var obfuscatedMessage = ObfuscateMessageReadability(message, 0.2f);
|
||||
|
||||
var transformSource = Transform(source);
|
||||
@@ -410,34 +416,8 @@ public sealed class ChatSystem : SharedChatSystem
|
||||
|
||||
private string SanitizeMessageCapital(EntityUid source, string message)
|
||||
{
|
||||
if (message.StartsWith(';'))
|
||||
{
|
||||
// Special case for ";" messages
|
||||
if (message.Length == 1)
|
||||
return "";
|
||||
|
||||
// Remove semicolon
|
||||
message = message.Substring(1).TrimStart();
|
||||
|
||||
// Capitalize first letter
|
||||
message = message[0].ToString().ToUpper() + message.Remove(0, 1);
|
||||
|
||||
if (_inventory.TryGetSlotEntity(source, "ears", out var entityUid) &&
|
||||
TryComp(entityUid, out HeadsetComponent? headset))
|
||||
{
|
||||
headset.RadioRequested = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("chat-manager-no-headset-on-message"), source, Filter.Entities(source));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Capitalize first letter
|
||||
message = message[0].ToString().ToUpper() + message.Remove(0, 1);
|
||||
}
|
||||
|
||||
// Capitalize first letter
|
||||
message = message[0].ToString().ToUpper() + message.Remove(0, 1);
|
||||
return message;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user