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
{