Move ChatSystem.Emotes to shared (#40866)
* move to shared * entity effect to shared * refactor: whitespaces+xml-doc typo fixups * refactor: a little bit more of xml-doc typos fixups --------- Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls transmission of chat.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.EntityEffects.Effects;
|
||||
|
||||
namespace Content.Server.EntityEffects.Effects;
|
||||
|
||||
/// <summary>
|
||||
/// Makes this entity emote.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||
public sealed partial class EmoteEntityEffectSystem : EntityEffectSystem<MetaDataComponent, Emote>
|
||||
{
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
|
||||
protected override void Effect(Entity<MetaDataComponent> entity, ref EntityEffectEvent<Emote> args)
|
||||
{
|
||||
if (args.Effect.ShowInChat)
|
||||
_chat.TryEmoteWithChat(entity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force);
|
||||
else
|
||||
_chat.TryEmoteWithoutChat(entity, args.Effect.EmoteId);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// An event raised just before an emote is performed, providing systems with an opportunity to cancel the emote's performance.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public sealed class BeforeEmoteEvent(EntityUid source, EmotePrototype emote)
|
||||
: CancellableEntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public readonly EntityUid Source = source;
|
||||
public readonly EmotePrototype Emote = emote;
|
||||
|
||||
/// <summary>
|
||||
/// The equipment that is blocking emoting. Should only be non-null if the event was canceled.
|
||||
/// </summary>
|
||||
public EntityUid? Blocker = null;
|
||||
|
||||
public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised by the chat system when an entity made some emote.
|
||||
/// Use it to play sound, change sprite or something else.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct EmoteEvent(EmotePrototype Emote)
|
||||
{
|
||||
/// <summary>
|
||||
/// The used emote.
|
||||
/// </summary>
|
||||
public EmotePrototype Emote = Emote;
|
||||
|
||||
/// <summary>
|
||||
/// If this message has already been "handled" by a previous system.
|
||||
/// </summary>
|
||||
public bool Handled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sent by the client when requesting the server to play a specific emote selected from the emote radial menu.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlayEmoteMessage(ProtoId<EmotePrototype> protoId) : EntityEventArgs
|
||||
{
|
||||
|
||||
@@ -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<string, EmotePrototype> _wordEmoteDict = FrozenDictionary<string, EmotePrototype>.Empty;
|
||||
|
||||
protected override void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
base.OnPrototypeReload(obj);
|
||||
if (obj.WasModified<EmotePrototype>())
|
||||
CacheEmotes();
|
||||
}
|
||||
|
||||
private void CacheEmotes()
|
||||
{
|
||||
var dict = new Dictionary<string, EmotePrototype>();
|
||||
@@ -47,15 +34,19 @@ public partial class ChatSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes selected entity to emote using <see cref="EmotePrototype"/> and sends message to chat.
|
||||
/// Makes the selected entity emote using the given <see cref="EmotePrototype"/> and sends a message to chat.
|
||||
/// </summary>
|
||||
/// <param name="source">The entity that is speaking</param>
|
||||
/// <param name="emoteId">The id of emote prototype. Should has valid <see cref="EmotePrototype.ChatMessages"/></param>
|
||||
/// <param name="hideLog">Whether or not this message should appear in the adminlog window</param>
|
||||
/// <param name="emoteId">The id of emote prototype. Should have valid <see cref="EmotePrototype.ChatMessages"/></param>
|
||||
/// <param name="hideLog">Whether this message should appear in the adminlog window, or not.</param>
|
||||
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
|
||||
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
|
||||
/// <param name="ignoreActionBlocker">Whether emote action blocking should be ignored or not.</param>
|
||||
/// <param name="nameOverride">
|
||||
/// The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>.
|
||||
/// If this is set, the event will not get raised.
|
||||
/// </param>
|
||||
/// <param name="forceEmote">Bypasses whitelist/blacklist/availibility checks for if the entity can use this emote</param>
|
||||
/// <returns>True if an emote was performed. False if the emote is unvailable, cancelled, etc.</returns>
|
||||
/// <returns>True if an emote was performed. False if the emote is unavailable, cancelled, etc.</returns>
|
||||
public bool TryEmoteWithChat(
|
||||
EntityUid source,
|
||||
string emoteId,
|
||||
@@ -66,22 +57,26 @@ public partial class ChatSystem
|
||||
bool forceEmote = false
|
||||
)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<EmotePrototype>(emoteId, out var proto))
|
||||
if (!_prototypeManager.Resolve<EmotePrototype>(emoteId, out var proto))
|
||||
return false;
|
||||
|
||||
return TryEmoteWithChat(source, proto, range, hideLog: hideLog, nameOverride, ignoreActionBlocker: ignoreActionBlocker, forceEmote: forceEmote);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes selected entity to emote using <see cref="EmotePrototype"/> and sends message to chat.
|
||||
/// Makes the selected entity emote using the given <see cref="EmotePrototype"/> and sends a message to chat.
|
||||
/// </summary>
|
||||
/// <param name="source">The entity that is speaking</param>
|
||||
/// <param name="emote">The emote prototype. Should has valid <see cref="EmotePrototype.ChatMessages"/></param>
|
||||
/// <param name="hideLog">Whether or not this message should appear in the adminlog window</param>
|
||||
/// <param name="hideChat">Whether or not this message should appear in the chat window</param>
|
||||
/// <param name="source">The entity that is speaking.</param>
|
||||
/// <param name="emote">The emote prototype. Should have valid <see cref="EmotePrototype.ChatMessages"/>.</param>
|
||||
/// <param name="hideLog">Whether this message should appear in the adminlog window or not.</param>
|
||||
/// <param name="ignoreActionBlocker">Whether emote action blocking should be ignored or not.</param>
|
||||
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
|
||||
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
|
||||
/// <param name="nameOverride">
|
||||
/// The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>.
|
||||
/// If this is set, the event will not get raised.
|
||||
/// </param>
|
||||
/// <param name="forceEmote">Bypasses whitelist/blacklist/availibility checks for if the entity can use this emote</param>
|
||||
/// <returns>True if an emote was performed. False if the emote is unvailable, cancelled, etc.</returns>
|
||||
/// <returns>True if an emote was performed. False if the emote is unavailable, cancelled, etc.</returns>
|
||||
public bool TryEmoteWithChat(
|
||||
EntityUid source,
|
||||
EmotePrototype emote,
|
||||
@@ -109,21 +104,21 @@ public partial class ChatSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes selected entity to emote using <see cref="EmotePrototype"/> without sending any messages to chat.
|
||||
/// Makes the selected entity emote using the given <see cref="EmotePrototype"/> without sending any messages to chat.
|
||||
/// </summary>
|
||||
/// <returns>True if an emote was performed. False if the emote is unvailable, cancelled, etc.</returns>
|
||||
/// <returns>True if an emote was performed. False if the emote is unavailable, cancelled, etc.</returns>
|
||||
public bool TryEmoteWithoutChat(EntityUid uid, string emoteId, bool ignoreActionBlocker = false)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<EmotePrototype>(emoteId, out var proto))
|
||||
if (!_prototypeManager.Resolve<EmotePrototype>(emoteId, out var proto))
|
||||
return false;
|
||||
|
||||
return TryEmoteWithoutChat(uid, proto, ignoreActionBlocker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes selected entity to emote using <see cref="EmotePrototype"/> without sending any messages to chat.
|
||||
/// Makes the selected entity emote using the given <see cref="EmotePrototype"/> without sending any messages to chat.
|
||||
/// </summary>
|
||||
/// <returns>True if an emote was performed. False if the emote is unvailable, cancelled, etc.</returns>
|
||||
/// <returns>True if an emote was performed. False if the emote is unavailable, cancelled, etc.</returns>
|
||||
public bool TryEmoteWithoutChat(EntityUid uid, EmotePrototype proto, bool ignoreActionBlocker = false)
|
||||
{
|
||||
if (!_actionBlocker.CanEmote(uid) && !ignoreActionBlocker)
|
||||
@@ -133,7 +128,7 @@ public partial class ChatSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>True if emote sound was played.</returns>
|
||||
public bool TryPlayEmoteSound(EntityUid uid, EmoteSoundsPrototype? proto, EmotePrototype emote, AudioParams? audioParams = null)
|
||||
@@ -142,7 +137,7 @@ public partial class ChatSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>True if emote sound was played.</returns>
|
||||
public bool TryPlayEmoteSound(EntityUid uid, EmoteSoundsPrototype? proto, string emoteId, AudioParams? audioParams = null)
|
||||
@@ -167,44 +162,27 @@ public partial class ChatSystem
|
||||
/// <summary>
|
||||
/// Checks if a valid emote was typed, to play sounds and etc and invokes an event.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="textInput"></param>
|
||||
/// <param name="source">The entity that is speaking</param>
|
||||
/// <param name="textInput">Formatted emote message.</param>
|
||||
/// <returns>True if the chat message should be displayed (because the emote was explicitly cancelled), false if it should not be.</returns>
|
||||
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];
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="source">The entity that is speaking</param>
|
||||
/// <param name="emote">The emote being used</param>
|
||||
/// <returns></returns>
|
||||
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<SpeechComponent>(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;
|
||||
}
|
||||
|
||||
private string TrimPunctuation(string textInput)
|
||||
{
|
||||
var trimEnd = textInput.Length;
|
||||
while (trimEnd > 0 && char.IsPunctuation(textInput[trimEnd - 1]))
|
||||
{
|
||||
trimEnd--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised by chat system when entity made some emote.
|
||||
/// Use it to play sound, change sprite or something else.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public sealed class EmoteEvent : HandledEntityEventArgs
|
||||
var trimStart = 0;
|
||||
while (trimStart < trimEnd && char.IsPunctuation(textInput[trimStart]))
|
||||
{
|
||||
public readonly EmotePrototype Emote;
|
||||
trimStart++;
|
||||
}
|
||||
|
||||
public EmoteEvent(EmotePrototype emote)
|
||||
{
|
||||
Emote = emote;
|
||||
Handled = false;
|
||||
return textInput[trimStart..trimEnd];
|
||||
}
|
||||
}
|
||||
@@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// 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<PrototypesReloadedEventArgs>(OnPrototypeReload);
|
||||
CacheRadios();
|
||||
CacheEmotes();
|
||||
}
|
||||
|
||||
protected virtual void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (obj.WasModified<RadioChannelPrototype>())
|
||||
CacheRadios();
|
||||
|
||||
if (obj.WasModified<EmotePrototype>())
|
||||
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
|
||||
)
|
||||
{ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls transmission of chat.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event raised just before an emote is performed, providing systems with an opportunity to cancel the emote's performance.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public sealed class BeforeEmoteEvent(EntityUid source, EmotePrototype emote)
|
||||
: CancellableEntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public readonly EntityUid Source = source;
|
||||
public readonly EmotePrototype Emote = emote;
|
||||
|
||||
/// <summary>
|
||||
/// The equipment that is blocking emoting. Should only be non-null if the event was canceled.
|
||||
/// </summary>
|
||||
public EntityUid? Blocker = null;
|
||||
|
||||
public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Makes this entity emote.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||
public sealed partial class EmoteEntityEffectSystem : EntityEffectSystem<MetaDataComponent, Emote>
|
||||
{
|
||||
[Dependency] private readonly SharedChatSystem _chat = default!;
|
||||
|
||||
protected override void Effect(Entity<MetaDataComponent> entity, ref EntityEffectEvent<Emote> args)
|
||||
{
|
||||
if (args.Effect.ShowInChat)
|
||||
_chat.TryEmoteWithChat(entity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force);
|
||||
else
|
||||
_chat.TryEmoteWithoutChat(entity, args.Effect.EmoteId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityEffect"/>
|
||||
public sealed partial class Emote : EntityEffectBase<Emote>
|
||||
{
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Suppresses emotes with the given categories or ID.
|
||||
/// Additionally, if the Scream Emote would be blocked, also blocks the Scream Action.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class EmoteBlockerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Which categories of emotes are blocked by this component.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public HashSet<EmoteCategory> BlocksCategories = [];
|
||||
|
||||
/// <summary>
|
||||
/// IDs of which specific emotes are blocked by this component.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public HashSet<ProtoId<EmotePrototype>> BlocksEmotes = [];
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
Reference in New Issue
Block a user