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 EmotesAltPrefix = '*'; 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!; /// /// Cache of the keycodes for faster lookup. /// private Dictionary _keyCodes = new(); public override void Initialize() { base.Initialize(); DebugTools.Assert(_prototypeManager.HasIndex(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()) { _keyCodes.Add(proto.KeyCode, proto); } } public override void Shutdown() { _prototypeManager.PrototypesReloaded -= OnPrototypeReload; } /// /// 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. /// /// Source of the message /// The message to be modified /// The modified message /// The channel that was requested, if any /// Whether or not to generate an informative pop-up message. /// 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(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; } }