diff --git a/Content.Server/Access/Systems/IdCardSystem.cs b/Content.Server/Access/Systems/IdCardSystem.cs index 0fef62d970..f317e88f0f 100644 --- a/Content.Server/Access/Systems/IdCardSystem.cs +++ b/Content.Server/Access/Systems/IdCardSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Popups; using Content.Shared.Access; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; +using Content.Shared.Chat; using Content.Shared.Database; using Content.Shared.Popups; using Robust.Shared.Prototypes; diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 2af7b24f26..ca3ee2c9e1 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Atmos; using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Prototypes; +using Content.Shared.Chat; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; diff --git a/Content.Server/Chat/Commands/MeCommand.cs b/Content.Server/Chat/Commands/MeCommand.cs index 36acfa7a69..f5b845a2d7 100644 --- a/Content.Server/Chat/Commands/MeCommand.cs +++ b/Content.Server/Chat/Commands/MeCommand.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Systems; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; using Robust.Shared.Enums; diff --git a/Content.Server/Chat/Commands/SayCommand.cs b/Content.Server/Chat/Commands/SayCommand.cs index 99ca4660f0..199cd69098 100644 --- a/Content.Server/Chat/Commands/SayCommand.cs +++ b/Content.Server/Chat/Commands/SayCommand.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Systems; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; using Robust.Shared.Enums; diff --git a/Content.Server/Chat/Commands/WhisperCommand.cs b/Content.Server/Chat/Commands/WhisperCommand.cs index 0556dd8036..d31b21abb1 100644 --- a/Content.Server/Chat/Commands/WhisperCommand.cs +++ b/Content.Server/Chat/Commands/WhisperCommand.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Systems; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; using Robust.Shared.Enums; diff --git a/Content.Server/Chat/Systems/AutoEmoteSystem.cs b/Content.Server/Chat/Systems/AutoEmoteSystem.cs index 1e1b7b9034..ebb22f6314 100644 --- a/Content.Server/Chat/Systems/AutoEmoteSystem.cs +++ b/Content.Server/Chat/Systems/AutoEmoteSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Random; diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index b073cc0e69..4ec86df54b 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -68,7 +68,7 @@ public sealed partial class ChatSystem : SharedChatSystem public override void Initialize() { base.Initialize(); - CacheEmotes(); + Subs.CVar(_configurationManager, CCVars.LoocEnabled, OnLoocEnabledChanged, true); Subs.CVar(_configurationManager, CCVars.DeadLoocEnabled, OnDeadLoocEnabledChanged, true); Subs.CVar(_configurationManager, CCVars.CritLoocEnabled, OnCritLoocEnabledChanged, true); @@ -563,7 +563,7 @@ public sealed partial class ChatSystem : SharedChatSystem } } - private void SendEntityEmote( + protected override void SendEntityEmote( EntityUid source, string action, ChatTransmitRange range, @@ -975,18 +975,3 @@ public enum InGameOOCChatType : byte Looc, Dead } - -/// -/// Controls transmission of chat. -/// -public enum ChatTransmitRange : byte -{ - /// Acts normal, ghosts can hear across the map, etc. - Normal, - /// Normal but ghosts are still range-limited. - GhostRangeLimit, - /// Hidden from the chat window. - HideChat, - /// Ghosts can't hear or see it at all. Regular players can if in-range. - NoGhosts -} diff --git a/Content.Server/Chat/Systems/EmoteOnDamageSystem.cs b/Content.Server/Chat/Systems/EmoteOnDamageSystem.cs index 878c517d92..4afb885c4c 100644 --- a/Content.Server/Chat/Systems/EmoteOnDamageSystem.cs +++ b/Content.Server/Chat/Systems/EmoteOnDamageSystem.cs @@ -1,5 +1,6 @@ namespace Content.Server.Chat.Systems; +using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Content.Shared.Damage; using Robust.Shared.Prototypes; diff --git a/Content.Server/Cluwne/CluwneSystem.cs b/Content.Server/Cluwne/CluwneSystem.cs index e51a01a1d4..ab13548e04 100644 --- a/Content.Server/Cluwne/CluwneSystem.cs +++ b/Content.Server/Cluwne/CluwneSystem.cs @@ -1,20 +1,21 @@ -using Content.Server.Popups; -using Content.Shared.Popups; -using Content.Shared.Mobs; using Content.Server.Chat; using Content.Server.Chat.Systems; -using Content.Server.Clothing.Systems; -using Content.Shared.Chat.Prototypes; -using Robust.Shared.Random; -using Content.Shared.Stunnable; -using Content.Shared.Damage; -using Robust.Shared.Prototypes; using Content.Server.Emoting.Systems; +using Content.Server.Clothing.Systems; +using Content.Server.Popups; using Content.Server.Speech.EntitySystems; -using Content.Shared.Cluwne; -using Robust.Shared.Audio.Systems; -using Content.Shared.NameModifier.EntitySystems; +using Content.Shared.Chat; +using Content.Shared.Chat.Prototypes; using Content.Shared.Clumsy; +using Content.Shared.Cluwne; +using Content.Shared.Damage; +using Content.Shared.Mobs; +using Content.Shared.NameModifier.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Stunnable; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; +using Robust.Shared.Prototypes; namespace Content.Server.Cluwne; @@ -75,7 +76,7 @@ public sealed class CluwneSystem : EntitySystem EnsureComp(ent.Owner); _autoEmote.AddEmote(ent.Owner, ent.Comp.AutoEmoteId); } - + EnsureComp(ent.Owner); var transformMessage = Loc.GetString(ent.Comp.TransformMessage, ("target", ent.Owner)); diff --git a/Content.Server/Emoting/Systems/BodyEmotesSystem.cs b/Content.Server/Emoting/Systems/BodyEmotesSystem.cs index aef79f1419..39217a8196 100644 --- a/Content.Server/Emoting/Systems/BodyEmotesSystem.cs +++ b/Content.Server/Emoting/Systems/BodyEmotesSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Systems; using Content.Server.Emoting.Components; +using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Content.Shared.Hands.Components; using Robust.Shared.Prototypes; diff --git a/Content.Server/EntityEffects/Effects/EmoteEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/EmoteEntityEffectSystem.cs deleted file mode 100644 index 05ab857267..0000000000 --- a/Content.Server/EntityEffects/Effects/EmoteEntityEffectSystem.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Server.Chat.Systems; -using Content.Shared.EntityEffects; -using Content.Shared.EntityEffects.Effects; - -namespace Content.Server.EntityEffects.Effects; - -/// -/// Makes this entity emote. -/// -/// -public sealed partial class EmoteEntityEffectSystem : EntityEffectSystem -{ - [Dependency] private readonly ChatSystem _chat = default!; - - protected override void Effect(Entity entity, ref EntityEffectEvent args) - { - if (args.Effect.ShowInChat) - _chat.TryEmoteWithChat(entity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force); - else - _chat.TryEmoteWithoutChat(entity, args.Effect.EmoteId); - } -} diff --git a/Content.Server/Holopad/HolopadSystem.cs b/Content.Server/Holopad/HolopadSystem.cs index 630141d003..c634d14f2f 100644 --- a/Content.Server/Holopad/HolopadSystem.cs +++ b/Content.Server/Holopad/HolopadSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Power.EntitySystems; using Content.Server.Telephone; using Content.Shared.Access.Systems; using Content.Shared.Audio; +using Content.Shared.Chat; using Content.Shared.Chat.TypingIndicator; using Content.Shared.Holopad; using Content.Shared.IdentityManagement; diff --git a/Content.Server/Mobs/CritMobActionsSystem.cs b/Content.Server/Mobs/CritMobActionsSystem.cs index c266037a8f..4a16fa19a3 100644 --- a/Content.Server/Mobs/CritMobActionsSystem.cs +++ b/Content.Server/Mobs/CritMobActionsSystem.cs @@ -1,7 +1,7 @@ using Content.Server.Administration; using Content.Server.Chat.Systems; using Content.Server.Popups; -using Content.Server.Speech.Muting; +using Content.Shared.Chat; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; diff --git a/Content.Server/Speech/EmotesMenuSystem.cs b/Content.Server/Speech/EmotesMenuSystem.cs index 5f91742eb1..6571587b91 100644 --- a/Content.Server/Speech/EmotesMenuSystem.cs +++ b/Content.Server/Speech/EmotesMenuSystem.cs @@ -1,5 +1,5 @@ -using Content.Server.Chat.Systems; -using Content.Shared.Chat; +using Content.Shared.Chat; +using Content.Server.Chat.Systems; using Robust.Shared.Prototypes; namespace Content.Server.Speech; diff --git a/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs b/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs index 27679c9134..868e5b73c1 100644 --- a/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Systems; using Content.Server.Speech.Components; +using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Content.Shared.Speech; using Content.Shared.Speech.Components; diff --git a/Content.Server/Speech/EntitySystems/VocalSystem.cs b/Content.Server/Speech/EntitySystems/VocalSystem.cs index 275140ff5b..2c83108898 100644 --- a/Content.Server/Speech/EntitySystems/VocalSystem.cs +++ b/Content.Server/Speech/EntitySystems/VocalSystem.cs @@ -1,12 +1,10 @@ using Content.Server.Actions; using Content.Server.Chat.Systems; -using Content.Server.Speech.Components; +using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; -using Content.Shared.Cloning.Events; using Content.Shared.Humanoid; using Content.Shared.Speech; using Content.Shared.Speech.Components; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; diff --git a/Content.Server/Speech/Muting/MutingSystem.cs b/Content.Server/Speech/Muting/MutingSystem.cs index f588e2238d..9e2a0602a4 100644 --- a/Content.Server/Speech/Muting/MutingSystem.cs +++ b/Content.Server/Speech/Muting/MutingSystem.cs @@ -1,8 +1,7 @@ -using Content.Shared.Abilities.Mime; -using Content.Server.Chat.Systems; using Content.Server.Popups; -using Content.Server.Speech.Components; using Content.Server.Speech.EntitySystems; +using Content.Shared.Abilities.Mime; +using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Content.Shared.Puppet; using Content.Shared.Speech; diff --git a/Content.Server/Teleportation/TeleportLocationsSystem.cs b/Content.Server/Teleportation/TeleportLocationsSystem.cs index 14211d9672..edda0859ee 100644 --- a/Content.Server/Teleportation/TeleportLocationsSystem.cs +++ b/Content.Server/Teleportation/TeleportLocationsSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Chat.Systems; +using Content.Shared.Chat; using Content.Shared.Teleportation; using Content.Shared.Teleportation.Components; using Content.Shared.Teleportation.Systems; diff --git a/Content.Server/Vocalization/Systems/VocalizationSystem.cs b/Content.Server/Vocalization/Systems/VocalizationSystem.cs index 49dfaf4281..8801e057b7 100644 --- a/Content.Server/Vocalization/Systems/VocalizationSystem.cs +++ b/Content.Server/Vocalization/Systems/VocalizationSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Chat.Systems; using Content.Server.Power.Components; using Content.Server.Vocalization.Components; using Content.Shared.ActionBlocker; +using Content.Shared.Chat; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index c182d53f44..b8f2bd56ed 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Anomaly.Components; using Content.Shared.Armor; using Content.Shared.Bed.Sleep; using Content.Shared.Cloning.Events; +using Content.Shared.Chat; using Content.Shared.Damage; using Content.Shared.Humanoid; using Content.Shared.Inventory; diff --git a/Content.Shared/Chat/EmotesEvents.cs b/Content.Shared/Chat/EmotesEvents.cs index 4479f8b2ab..196bbb43bd 100644 --- a/Content.Shared/Chat/EmotesEvents.cs +++ b/Content.Shared/Chat/EmotesEvents.cs @@ -1,9 +1,49 @@ -using Content.Shared.Chat.Prototypes; +using Content.Shared.Chat.Prototypes; +using Content.Shared.Inventory; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Chat; +/// +/// An event raised just before an emote is performed, providing systems with an opportunity to cancel the emote's performance. +/// +[ByRefEvent] +public sealed class BeforeEmoteEvent(EntityUid source, EmotePrototype emote) + : CancellableEntityEventArgs, IInventoryRelayEvent +{ + public readonly EntityUid Source = source; + public readonly EmotePrototype Emote = emote; + + /// + /// The equipment that is blocking emoting. Should only be non-null if the event was canceled. + /// + public EntityUid? Blocker = null; + + public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET; +} + +/// +/// Raised by the chat system when an entity made some emote. +/// Use it to play sound, change sprite or something else. +/// +[ByRefEvent] +public record struct EmoteEvent(EmotePrototype Emote) +{ + /// + /// The used emote. + /// + public EmotePrototype Emote = Emote; + + /// + /// If this message has already been "handled" by a previous system. + /// + public bool Handled; +} + +/// +/// Sent by the client when requesting the server to play a specific emote selected from the emote radial menu. +/// [Serializable, NetSerializable] public sealed class PlayEmoteMessage(ProtoId protoId) : EntityEventArgs { diff --git a/Content.Server/Chat/Systems/ChatSystem.Emote.cs b/Content.Shared/Chat/SharedChatSystem.Emote.cs similarity index 68% rename from Content.Server/Chat/Systems/ChatSystem.Emote.cs rename to Content.Shared/Chat/SharedChatSystem.Emote.cs index ee891e0870..d92a02a9de 100644 --- a/Content.Server/Chat/Systems/ChatSystem.Emote.cs +++ b/Content.Shared/Chat/SharedChatSystem.Emote.cs @@ -1,28 +1,15 @@ using System.Collections.Frozen; -using Content.Server.Popups; using Content.Shared.Chat.Prototypes; -using Content.Shared.Emoting; using Content.Shared.Speech; using Robust.Shared.Audio; -using Robust.Shared.Prototypes; using Robust.Shared.Random; -namespace Content.Server.Chat.Systems; +namespace Content.Shared.Chat; -// emotes using emote prototype -public partial class ChatSystem +public abstract partial class SharedChatSystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; - private FrozenDictionary _wordEmoteDict = FrozenDictionary.Empty; - protected override void OnPrototypeReload(PrototypesReloadedEventArgs obj) - { - base.OnPrototypeReload(obj); - if (obj.WasModified()) - CacheEmotes(); - } - private void CacheEmotes() { var dict = new Dictionary(); @@ -47,15 +34,19 @@ public partial class ChatSystem } /// - /// Makes selected entity to emote using and sends message to chat. + /// Makes the selected entity emote using the given and sends a message to chat. /// /// The entity that is speaking - /// The id of emote prototype. Should has valid - /// Whether or not this message should appear in the adminlog window + /// The id of emote prototype. Should have valid + /// Whether this message should appear in the adminlog window, or not. /// Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all... - /// The name to use for the speaking entity. Usually this should just be modified via . If this is set, the event will not get raised. + /// Whether emote action blocking should be ignored or not. + /// + /// The name to use for the speaking entity. Usually this should just be modified via . + /// If this is set, the event will not get raised. + /// /// Bypasses whitelist/blacklist/availibility checks for if the entity can use this emote - /// True if an emote was performed. False if the emote is unvailable, cancelled, etc. + /// True if an emote was performed. False if the emote is unavailable, cancelled, etc. public bool TryEmoteWithChat( EntityUid source, string emoteId, @@ -64,24 +55,28 @@ public partial class ChatSystem string? nameOverride = null, bool ignoreActionBlocker = false, bool forceEmote = false - ) + ) { - if (!_prototypeManager.TryIndex(emoteId, out var proto)) + if (!_prototypeManager.Resolve(emoteId, out var proto)) return false; + return TryEmoteWithChat(source, proto, range, hideLog: hideLog, nameOverride, ignoreActionBlocker: ignoreActionBlocker, forceEmote: forceEmote); } /// - /// Makes selected entity to emote using and sends message to chat. + /// Makes the selected entity emote using the given and sends a message to chat. /// - /// The entity that is speaking - /// The emote prototype. Should has valid - /// Whether or not this message should appear in the adminlog window - /// Whether or not this message should appear in the chat window + /// The entity that is speaking. + /// The emote prototype. Should have valid . + /// Whether this message should appear in the adminlog window or not. + /// Whether emote action blocking should be ignored or not. /// Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all... - /// The name to use for the speaking entity. Usually this should just be modified via . If this is set, the event will not get raised. + /// + /// The name to use for the speaking entity. Usually this should just be modified via . + /// If this is set, the event will not get raised. + /// /// Bypasses whitelist/blacklist/availibility checks for if the entity can use this emote - /// True if an emote was performed. False if the emote is unvailable, cancelled, etc. + /// True if an emote was performed. False if the emote is unavailable, cancelled, etc. public bool TryEmoteWithChat( EntityUid source, EmotePrototype emote, @@ -109,21 +104,21 @@ public partial class ChatSystem } /// - /// Makes selected entity to emote using without sending any messages to chat. + /// Makes the selected entity emote using the given without sending any messages to chat. /// - /// True if an emote was performed. False if the emote is unvailable, cancelled, etc. + /// True if an emote was performed. False if the emote is unavailable, cancelled, etc. public bool TryEmoteWithoutChat(EntityUid uid, string emoteId, bool ignoreActionBlocker = false) { - if (!_prototypeManager.TryIndex(emoteId, out var proto)) + if (!_prototypeManager.Resolve(emoteId, out var proto)) return false; return TryEmoteWithoutChat(uid, proto, ignoreActionBlocker); } /// - /// Makes selected entity to emote using without sending any messages to chat. + /// Makes the selected entity emote using the given without sending any messages to chat. /// - /// True if an emote was performed. False if the emote is unvailable, cancelled, etc. + /// True if an emote was performed. False if the emote is unavailable, cancelled, etc. public bool TryEmoteWithoutChat(EntityUid uid, EmotePrototype proto, bool ignoreActionBlocker = false) { if (!_actionBlocker.CanEmote(uid) && !ignoreActionBlocker) @@ -133,7 +128,7 @@ public partial class ChatSystem } /// - /// Tries to find and play relevant emote sound in emote sounds collection. + /// Tries to find and play the relevant emote sound in an emote sounds collection. /// /// True if emote sound was played. public bool TryPlayEmoteSound(EntityUid uid, EmoteSoundsPrototype? proto, EmotePrototype emote, AudioParams? audioParams = null) @@ -142,7 +137,7 @@ public partial class ChatSystem } /// - /// Tries to find and play relevant emote sound in emote sounds collection. + /// Tries to find and play the relevant emote sound in an emote sounds collection. /// /// True if emote sound was played. public bool TryPlayEmoteSound(EntityUid uid, EmoteSoundsPrototype? proto, string emoteId, AudioParams? audioParams = null) @@ -167,44 +162,27 @@ public partial class ChatSystem /// /// Checks if a valid emote was typed, to play sounds and etc and invokes an event. /// - /// - /// + /// The entity that is speaking + /// Formatted emote message. /// True if the chat message should be displayed (because the emote was explicitly cancelled), false if it should not be. - private bool TryEmoteChatInput(EntityUid uid, string textInput) + protected bool TryEmoteChatInput(EntityUid source, string textInput) { var actionTrimmedLower = TrimPunctuation(textInput.ToLower()); if (!_wordEmoteDict.TryGetValue(actionTrimmedLower, out var emote)) return true; - if (!AllowedToUseEmote(uid, emote)) + if (!AllowedToUseEmote(source, emote)) return true; - return TryInvokeEmoteEvent(uid, emote); + return TryInvokeEmoteEvent(source, emote); - static string TrimPunctuation(string textInput) - { - var trimEnd = textInput.Length; - while (trimEnd > 0 && char.IsPunctuation(textInput[trimEnd - 1])) - { - trimEnd--; - } - - var trimStart = 0; - while (trimStart < trimEnd && char.IsPunctuation(textInput[trimStart])) - { - trimStart++; - } - - return textInput[trimStart..trimEnd]; - } } /// - /// Checks if we can use this emote based on the emotes whitelist, blacklist, and availibility to the entity. + /// Checks if we can use this emote based on the emotes whitelist, blacklist, and availability to the entity. /// /// The entity that is speaking /// The emote being used - /// - private bool AllowedToUseEmote(EntityUid source, EmotePrototype emote) + public bool AllowedToUseEmote(EntityUid source, EmotePrototype emote) { // If emote is in AllowedEmotes, it will bypass whitelist and blacklist if (TryComp(source, out var speech) && @@ -214,8 +192,8 @@ public partial class ChatSystem } // Check the whitelist and blacklist - if (_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) || - _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)) + if (_whitelist.IsWhitelistFail(emote.Whitelist, source) || + _whitelist.IsBlacklistPass(emote.Blacklist, source)) { return false; } @@ -244,9 +222,13 @@ public partial class ChatSystem if (beforeEv.Cancelled) { + // Chat is not predicted anyways, so no need to predict this popup either. + if (_net.IsClient) + return false; + if (beforeEv.Blocker != null) { - _popupSystem.PopupEntity( + _popup.PopupEntity( Loc.GetString( "chat-system-emote-cancelled-blocked", ("emote", Loc.GetString(proto.Name).ToLower()), @@ -258,7 +240,7 @@ public partial class ChatSystem } else { - _popupSystem.PopupEntity( + _popup.PopupEntity( Loc.GetString("chat-system-emote-cancelled-generic", ("emote", Loc.GetString(proto.Name).ToLower())), uid, @@ -274,20 +256,21 @@ public partial class ChatSystem return true; } -} -/// -/// Raised by chat system when entity made some emote. -/// Use it to play sound, change sprite or something else. -/// -[ByRefEvent] -public sealed class EmoteEvent : HandledEntityEventArgs -{ - public readonly EmotePrototype Emote; - - public EmoteEvent(EmotePrototype emote) + private string TrimPunctuation(string textInput) { - Emote = emote; - Handled = false; + var trimEnd = textInput.Length; + while (trimEnd > 0 && char.IsPunctuation(textInput[trimEnd - 1])) + { + trimEnd--; + } + + var trimStart = 0; + while (trimStart < trimEnd && char.IsPunctuation(textInput[trimStart])) + { + trimStart++; + } + + return textInput[trimStart..trimEnd]; } } diff --git a/Content.Shared/Chat/SharedChatSystem.cs b/Content.Shared/Chat/SharedChatSystem.cs index d9f7f5fc57..eca84249c8 100644 --- a/Content.Shared/Chat/SharedChatSystem.cs +++ b/Content.Shared/Chat/SharedChatSystem.cs @@ -1,15 +1,21 @@ using System.Collections.Frozen; using System.Text.RegularExpressions; +using Content.Shared.ActionBlocker; +using Content.Shared.Chat.Prototypes; using Content.Shared.Popups; using Content.Shared.Radio; using Content.Shared.Speech; +using Content.Shared.Whitelist; using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; using Robust.Shared.Prototypes; +using Robust.Shared.Random; using Robust.Shared.Utility; namespace Content.Shared.Chat; -public abstract class SharedChatSystem : EntitySystem +public abstract partial class SharedChatSystem : EntitySystem { public const char RadioCommonPrefix = ';'; public const char RadioChannelPrefix = ':'; @@ -38,6 +44,11 @@ public abstract class SharedChatSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly INetManager _net = default!; /// /// Cache of the keycodes for faster lookup. @@ -47,15 +58,21 @@ public abstract class SharedChatSystem : EntitySystem public override void Initialize() { base.Initialize(); + DebugTools.Assert(_prototypeManager.HasIndex(CommonChannel)); + SubscribeLocalEvent(OnPrototypeReload); CacheRadios(); + CacheEmotes(); } protected virtual void OnPrototypeReload(PrototypesReloadedEventArgs obj) { if (obj.WasModified()) CacheRadios(); + + if (obj.WasModified()) + CacheEmotes(); } private void CacheRadios() @@ -293,4 +310,31 @@ public abstract class SharedChatSystem : EntitySystem tagStart += tag.Length + 2; return rawmsg.Substring(tagStart, tagEnd - tagStart); } + + protected virtual void SendEntityEmote( + EntityUid source, + string action, + ChatTransmitRange range, + string? nameOverride, + bool hideLog = false, + bool checkEmote = true, + bool ignoreActionBlocker = false, + NetUserId? author = null + ) + { } +} + +/// +/// Controls transmission of chat. +/// +public enum ChatTransmitRange : byte +{ + /// Acts normal, ghosts can hear across the map, etc. + Normal, + /// Normal but ghosts are still range-limited. + GhostRangeLimit, + /// Hidden from the chat window. + HideChat, + /// Ghosts can't hear or see it at all. Regular players can if in-range. + NoGhosts } diff --git a/Content.Shared/Emoting/EmoteEvents.cs b/Content.Shared/Emoting/EmoteEvents.cs index ea3073f336..931395ee7e 100644 --- a/Content.Shared/Emoting/EmoteEvents.cs +++ b/Content.Shared/Emoting/EmoteEvents.cs @@ -1,27 +1,6 @@ -using Content.Shared.Chat.Prototypes; -using Content.Shared.Inventory; - -namespace Content.Shared.Emoting; +namespace Content.Shared.Emoting; public sealed class EmoteAttemptEvent(EntityUid uid) : CancellableEntityEventArgs { public EntityUid Uid { get; } = uid; } - -/// -/// An event raised just before an emote is performed, providing systems with an opportunity to cancel the emote's performance. -/// -[ByRefEvent] -public sealed class BeforeEmoteEvent(EntityUid source, EmotePrototype emote) - : CancellableEntityEventArgs, IInventoryRelayEvent -{ - public readonly EntityUid Source = source; - public readonly EmotePrototype Emote = emote; - - /// - /// The equipment that is blocking emoting. Should only be non-null if the event was canceled. - /// - public EntityUid? Blocker = null; - - public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET; -} diff --git a/Content.Shared/EntityEffects/Effects/EmoteEntityEffect.cs b/Content.Shared/EntityEffects/Effects/EmoteEntityEffectSystem.cs similarity index 63% rename from Content.Shared/EntityEffects/Effects/EmoteEntityEffect.cs rename to Content.Shared/EntityEffects/Effects/EmoteEntityEffectSystem.cs index 1fcfc703ce..d8474e4163 100644 --- a/Content.Shared/EntityEffects/Effects/EmoteEntityEffect.cs +++ b/Content.Shared/EntityEffects/Effects/EmoteEntityEffectSystem.cs @@ -1,8 +1,26 @@ -using Content.Shared.Chat.Prototypes; +using Content.Shared.Chat; +using Content.Shared.Chat.Prototypes; using Robust.Shared.Prototypes; namespace Content.Shared.EntityEffects.Effects; +/// +/// Makes this entity emote. +/// +/// +public sealed partial class EmoteEntityEffectSystem : EntityEffectSystem +{ + [Dependency] private readonly SharedChatSystem _chat = default!; + + protected override void Effect(Entity entity, ref EntityEffectEvent args) + { + if (args.Effect.ShowInChat) + _chat.TryEmoteWithChat(entity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force); + else + _chat.TryEmoteWithoutChat(entity, args.Effect.EmoteId); + } +} + /// public sealed partial class Emote : EntityEffectBase { diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 5109930a2d..392234297c 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -8,7 +8,6 @@ using Content.Shared.Contraband; using Content.Shared.Damage; using Content.Shared.Damage.Events; using Content.Shared.Electrocution; -using Content.Shared.Emoting; using Content.Shared.Explosion; using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Flash; diff --git a/Content.Server/Speech/Components/EmoteBlockerComponent.cs b/Content.Shared/Speech/Components/EmoteBlockerComponent.cs similarity index 74% rename from Content.Server/Speech/Components/EmoteBlockerComponent.cs rename to Content.Shared/Speech/Components/EmoteBlockerComponent.cs index 603fd1b258..95263a2595 100644 --- a/Content.Server/Speech/Components/EmoteBlockerComponent.cs +++ b/Content.Shared/Speech/Components/EmoteBlockerComponent.cs @@ -1,24 +1,25 @@ using Content.Shared.Chat.Prototypes; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -namespace Content.Server.Speech.Components; +namespace Content.Shared.Speech.Components; /// /// Suppresses emotes with the given categories or ID. /// Additionally, if the Scream Emote would be blocked, also blocks the Scream Action. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class EmoteBlockerComponent : Component { /// /// Which categories of emotes are blocked by this component. /// - [DataField] + [DataField, AutoNetworkedField] public HashSet BlocksCategories = []; /// /// IDs of which specific emotes are blocked by this component. /// - [DataField] + [DataField, AutoNetworkedField] public HashSet> BlocksEmotes = []; } diff --git a/Content.Server/Speech/EntitySystems/EmoteBlockerSystem.cs b/Content.Shared/Speech/EntitySystems/EmoteBlockerSystem.cs similarity index 90% rename from Content.Server/Speech/EntitySystems/EmoteBlockerSystem.cs rename to Content.Shared/Speech/EntitySystems/EmoteBlockerSystem.cs index 3599268359..e2c9728fbc 100644 --- a/Content.Server/Speech/EntitySystems/EmoteBlockerSystem.cs +++ b/Content.Shared/Speech/EntitySystems/EmoteBlockerSystem.cs @@ -1,8 +1,8 @@ -using Content.Server.Speech.Components; -using Content.Shared.Emoting; +using Content.Shared.Chat; using Content.Shared.Inventory; +using Content.Shared.Speech.Components; -namespace Content.Server.Speech.EntitySystems; +namespace Content.Shared.Speech.EntitySystems; public sealed class EmoteBlockerSystem : EntitySystem {