From 16b3e2233a43ce5130d000f5ac7a57d9d2509cdd Mon Sep 17 00:00:00 2001 From: Morb <14136326+Morb0@users.noreply.github.com> Date: Mon, 29 Apr 2024 07:38:23 +0300 Subject: [PATCH] Emotes Menu (#26702) * Basic emote radial menu * Move out from corvax * Move to UI controller & add to top menu bar and key bind * Make emote play * Add name localization for emotes * Localize chat messages * Fix emote menu * Add categories localization * Fixes * Fix * Add emotes entity blacklist * Fix entity whitelist required all logic * Remove unused wagging emote * Revert sprite * Set default texture for emote icon * Update Resources/keybinds.yml --------- Co-authored-by: Kara --- Content.Client/Chat/UI/EmotesMenu.xaml | 31 +++ Content.Client/Chat/UI/EmotesMenu.xaml.cs | 112 ++++++++++ Content.Client/Input/ContentContexts.cs | 1 + .../Systems/Emotes/EmotesUIController.cs | 125 ++++++++++++ .../MenuBar/GameTopMenuBarUIController.cs | 4 + .../MenuBar/Widgets/GameTopMenuBar.xaml | 10 + .../Chat/Systems/ChatSystem.Emote.cs | 11 + Content.Server/Speech/EmotesMenuSystem.cs | 30 +++ .../Speech/EntitySystems/VocalSystem.cs | 1 + Content.Shared/Chat/EmotesEvents.cs | 11 + .../Chat/Prototypes/EmotePrototype.cs | 42 +++- .../Chat/Prototypes/EmoteSoundsPrototype.cs | 5 +- Content.Shared/Input/ContentKeyFunctions.cs | 1 + .../Speech/Components/VocalComponent.cs | 15 +- Content.Shared/Speech/SpeechComponent.cs | 8 + Content.Shared/Wagging/WaggingComponent.cs | 3 - Content.Shared/Whitelist/EntityWhitelist.cs | 3 + Resources/Locale/en-US/HUD/game-hud.ftl | 1 + Resources/Locale/en-US/chat/emotes.ftl | 60 ++++++ Resources/Locale/en-US/chat/ui/emote-menu.ftl | 3 + Resources/Locale/en-US/emotes/emotes.ftl | 1 - .../Prototypes/Entities/Mobs/NPCs/animals.yml | 6 + .../Prototypes/Entities/Mobs/NPCs/slimes.yml | 6 + .../Prototypes/Entities/Mobs/NPCs/space.yml | 1 + .../Entities/Mobs/Species/arachnid.yml | 1 + .../Prototypes/Entities/Mobs/Species/moth.yml | 1 + .../Entities/Mobs/Species/slime.yml | 1 + Resources/Prototypes/Voice/disease_emotes.yml | 38 +++- Resources/Prototypes/Voice/speech_emotes.yml | 193 ++++++++++++++++-- Resources/Prototypes/Voice/tail_emotes.yml | 14 -- Resources/Textures/Interface/emotes.svg | 1 + .../Textures/Interface/emotes.svg.192dpi.png | Bin 0 -> 1818 bytes .../Interface/emotes.svg.192dpi.png.yml | 2 + Resources/keybinds.yml | 3 + 34 files changed, 688 insertions(+), 57 deletions(-) create mode 100644 Content.Client/Chat/UI/EmotesMenu.xaml create mode 100644 Content.Client/Chat/UI/EmotesMenu.xaml.cs create mode 100644 Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs create mode 100644 Content.Server/Speech/EmotesMenuSystem.cs create mode 100644 Content.Shared/Chat/EmotesEvents.cs rename {Content.Server => Content.Shared}/Speech/Components/VocalComponent.cs (83%) create mode 100644 Resources/Locale/en-US/chat/emotes.ftl create mode 100644 Resources/Locale/en-US/chat/ui/emote-menu.ftl delete mode 100644 Resources/Locale/en-US/emotes/emotes.ftl delete mode 100644 Resources/Prototypes/Voice/tail_emotes.yml create mode 100644 Resources/Textures/Interface/emotes.svg create mode 100644 Resources/Textures/Interface/emotes.svg.192dpi.png create mode 100644 Resources/Textures/Interface/emotes.svg.192dpi.png.yml diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml b/Content.Client/Chat/UI/EmotesMenu.xaml new file mode 100644 index 0000000000..819a6543c4 --- /dev/null +++ b/Content.Client/Chat/UI/EmotesMenu.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml.cs b/Content.Client/Chat/UI/EmotesMenu.xaml.cs new file mode 100644 index 0000000000..a26d319920 --- /dev/null +++ b/Content.Client/Chat/UI/EmotesMenu.xaml.cs @@ -0,0 +1,112 @@ +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.Chat.Prototypes; +using Content.Shared.Speech; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Client.Chat.UI; + +[GenerateTypedNameReferences] +public sealed partial class EmotesMenu : RadialMenu +{ + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + + private readonly SpriteSystem _spriteSystem; + + public event Action>? OnPlayEmote; + + public EmotesMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + _spriteSystem = _entManager.System(); + + var main = FindControl("Main"); + + var emotes = _prototypeManager.EnumeratePrototypes(); + foreach (var emote in emotes) + { + var player = _playerManager.LocalSession?.AttachedEntity; + if (emote.Category == EmoteCategory.Invalid || + emote.ChatTriggers.Count == 0 || + !(player.HasValue && (emote.Whitelist?.IsValid(player.Value, _entManager) ?? true)) || + (emote.Blacklist?.IsValid(player.Value, _entManager) ?? false)) + continue; + + if (!emote.Available && + _entManager.TryGetComponent(player.Value, out var speech) && + !speech.AllowedEmotes.Contains(emote.ID)) + continue; + + var parent = FindControl(emote.Category.ToString()); + + var button = new EmoteMenuButton + { + StyleClasses = { "RadialMenuButton" }, + SetSize = new Vector2(64f, 64f), + ToolTip = Loc.GetString(emote.Name), + ProtoId = emote.ID, + }; + + var tex = new TextureRect + { + VerticalAlignment = VAlignment.Center, + HorizontalAlignment = HAlignment.Center, + Texture = _spriteSystem.Frame0(emote.Icon), + TextureScale = new Vector2(2f, 2f), + }; + + button.AddChild(tex); + parent.AddChild(button); + foreach (var child in main.Children) + { + if (child is not RadialMenuTextureButton castChild) + continue; + + if (castChild.TargetLayer == emote.Category.ToString()) + { + castChild.Visible = true; + break; + } + } + } + + + // Set up menu actions + foreach (var child in Children) + { + if (child is not RadialContainer container) + continue; + AddEmoteClickAction(container); + } + } + + private void AddEmoteClickAction(RadialContainer container) + { + foreach (var child in container.Children) + { + if (child is not EmoteMenuButton castChild) + continue; + + castChild.OnButtonUp += _ => + { + OnPlayEmote?.Invoke(castChild.ProtoId); + Close(); + }; + } + } +} + + +public sealed class EmoteMenuButton : RadialMenuTextureButton +{ + public ProtoId ProtoId { get; set; } +} diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 8a7ca3b773..7a8a993854 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -60,6 +60,7 @@ namespace Content.Client.Input human.AddFunction(ContentKeyFunctions.UseItemInHand); human.AddFunction(ContentKeyFunctions.AltUseItemInHand); human.AddFunction(ContentKeyFunctions.OpenCharacterMenu); + human.AddFunction(ContentKeyFunctions.OpenEmotesMenu); human.AddFunction(ContentKeyFunctions.ActivateItemInWorld); human.AddFunction(ContentKeyFunctions.ThrowItemInHand); human.AddFunction(ContentKeyFunctions.AltActivateItemInWorld); diff --git a/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs b/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs new file mode 100644 index 0000000000..7b86859a1a --- /dev/null +++ b/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs @@ -0,0 +1,125 @@ +using Content.Client.Chat.UI; +using Content.Client.Gameplay; +using Content.Client.UserInterface.Controls; +using Content.Shared.Chat; +using Content.Shared.Chat.Prototypes; +using Content.Shared.Input; +using JetBrains.Annotations; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface.Controllers; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Input.Binding; +using Robust.Shared.Prototypes; + +namespace Content.Client.UserInterface.Systems.Emotes; + +[UsedImplicitly] +public sealed class EmotesUIController : UIController, IOnStateChanged +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + + private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull()?.EmotesButton; + private EmotesMenu? _menu; + + public void OnStateEntered(GameplayState state) + { + CommandBinds.Builder + .Bind(ContentKeyFunctions.OpenEmotesMenu, + InputCmdHandler.FromDelegate(_ => ToggleEmotesMenu(false))) + .Register(); + } + + public void OnStateExited(GameplayState state) + { + CommandBinds.Unregister(); + } + + private void ToggleEmotesMenu(bool centered) + { + if (_menu == null) + { + // setup window + _menu = UIManager.CreateWindow(); + _menu.OnClose += OnWindowClosed; + _menu.OnOpen += OnWindowOpen; + _menu.OnPlayEmote += OnPlayEmote; + + if (EmotesButton != null) + EmotesButton.SetClickPressed(true); + + if (centered) + { + _menu.OpenCentered(); + } + else + { + // Open the menu, centered on the mouse + var vpSize = _displayManager.ScreenSize; + _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize); + } + } + else + { + _menu.OnClose -= OnWindowClosed; + _menu.OnOpen -= OnWindowOpen; + _menu.OnPlayEmote -= OnPlayEmote; + + if (EmotesButton != null) + EmotesButton.SetClickPressed(false); + + CloseMenu(); + } + } + + public void UnloadButton() + { + if (EmotesButton == null) + return; + + EmotesButton.OnPressed -= ActionButtonPressed; + } + + public void LoadButton() + { + if (EmotesButton == null) + return; + + EmotesButton.OnPressed += ActionButtonPressed; + } + + private void ActionButtonPressed(BaseButton.ButtonEventArgs args) + { + ToggleEmotesMenu(true); + } + + private void OnWindowClosed() + { + if (EmotesButton != null) + EmotesButton.Pressed = false; + + CloseMenu(); + } + + private void OnWindowOpen() + { + if (EmotesButton != null) + EmotesButton.Pressed = true; + } + + private void CloseMenu() + { + if (_menu == null) + return; + + _menu.Dispose(); + _menu = null; + } + + private void OnPlayEmote(ProtoId protoId) + { + _entityManager.RaisePredictiveEvent(new PlayEmoteMessage(protoId)); + } +} diff --git a/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs b/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs index 1505db48a7..e314310bc0 100644 --- a/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs +++ b/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs @@ -3,6 +3,7 @@ using Content.Client.UserInterface.Systems.Admin; using Content.Client.UserInterface.Systems.Bwoink; using Content.Client.UserInterface.Systems.Character; using Content.Client.UserInterface.Systems.Crafting; +using Content.Client.UserInterface.Systems.Emotes; using Content.Client.UserInterface.Systems.EscapeMenu; using Content.Client.UserInterface.Systems.Gameplay; using Content.Client.UserInterface.Systems.Guidebook; @@ -22,6 +23,7 @@ public sealed class GameTopMenuBarUIController : UIController [Dependency] private readonly ActionUIController _action = default!; [Dependency] private readonly SandboxUIController _sandbox = default!; [Dependency] private readonly GuidebookUIController _guidebook = default!; + [Dependency] private readonly EmotesUIController _emotes = default!; private GameTopMenuBar? GameTopMenuBar => UIManager.GetActiveUIWidgetOrNull(); @@ -44,6 +46,7 @@ public sealed class GameTopMenuBarUIController : UIController _ahelp.UnloadButton(); _action.UnloadButton(); _sandbox.UnloadButton(); + _emotes.UnloadButton(); } public void LoadButtons() @@ -56,5 +59,6 @@ public sealed class GameTopMenuBarUIController : UIController _ahelp.LoadButton(); _action.LoadButton(); _sandbox.LoadButton(); + _emotes.LoadButton(); } } diff --git a/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml b/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml index 3c8cd1d164..dc8972970a 100644 --- a/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml +++ b/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml @@ -43,6 +43,16 @@ HorizontalExpand="True" AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}" /> + (source, out var speech) && + !speech.AllowedEmotes.Contains(emote.ID)) + return; + // check if proto has valid message for chat if (emote.ChatMessages.Count != 0) { diff --git a/Content.Server/Speech/EmotesMenuSystem.cs b/Content.Server/Speech/EmotesMenuSystem.cs new file mode 100644 index 0000000000..a69b5a65e4 --- /dev/null +++ b/Content.Server/Speech/EmotesMenuSystem.cs @@ -0,0 +1,30 @@ +using Content.Server.Chat.Systems; +using Content.Shared.Chat; +using Robust.Shared.Prototypes; + +namespace Content.Server.Speech; + +public sealed partial class EmotesMenuSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly ChatSystem _chat = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeAllEvent(OnPlayEmote); + } + + private void OnPlayEmote(PlayEmoteMessage msg, EntitySessionEventArgs args) + { + var player = args.SenderSession.AttachedEntity; + if (!player.HasValue) + return; + + if (!_prototypeManager.TryIndex(msg.ProtoId, out var proto) || proto.ChatTriggers.Count == 0) + return; + + _chat.TryEmoteWithChat(player.Value, msg.ProtoId); + } +} diff --git a/Content.Server/Speech/EntitySystems/VocalSystem.cs b/Content.Server/Speech/EntitySystems/VocalSystem.cs index aedcbbd099..7c8ec21a94 100644 --- a/Content.Server/Speech/EntitySystems/VocalSystem.cs +++ b/Content.Server/Speech/EntitySystems/VocalSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Speech.Components; using Content.Shared.Chat.Prototypes; 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; diff --git a/Content.Shared/Chat/EmotesEvents.cs b/Content.Shared/Chat/EmotesEvents.cs new file mode 100644 index 0000000000..4479f8b2ab --- /dev/null +++ b/Content.Shared/Chat/EmotesEvents.cs @@ -0,0 +1,11 @@ +using Content.Shared.Chat.Prototypes; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chat; + +[Serializable, NetSerializable] +public sealed class PlayEmoteMessage(ProtoId protoId) : EntityEventArgs +{ + public readonly ProtoId ProtoId = protoId; +} diff --git a/Content.Shared/Chat/Prototypes/EmotePrototype.cs b/Content.Shared/Chat/Prototypes/EmotePrototype.cs index 08f209d28d..7ee958ee6a 100644 --- a/Content.Shared/Chat/Prototypes/EmotePrototype.cs +++ b/Content.Shared/Chat/Prototypes/EmotePrototype.cs @@ -1,11 +1,13 @@ +using Content.Shared.Whitelist; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; +using Robust.Shared.Utility; namespace Content.Shared.Chat.Prototypes; /// /// IC emotes (scream, smile, clapping, etc). -/// Entities can activate emotes by chat input or code. +/// Entities can activate emotes by chat input, radial or code. /// [Prototype("emote")] public sealed partial class EmotePrototype : IPrototype @@ -13,18 +15,50 @@ public sealed partial class EmotePrototype : IPrototype [IdDataField] public string ID { get; private set; } = default!; + /// + /// Localization string for the emote name. Displayed in the radial UI. + /// + [DataField(required: true)] + public string Name = default!; + + /// + /// Determines if emote available to all by default + /// check comes after this setting + /// can ignore this setting + /// + [DataField] + public bool Available = true; + /// /// Different emote categories may be handled by different systems. /// Also may be used for filtering. /// - [DataField("category")] + [DataField] public EmoteCategory Category = EmoteCategory.General; + /// + /// An icon used to visually represent the emote in radial UI. + /// + [DataField] + public SpriteSpecifier Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/Actions/scream.png")); + + /// + /// Determines conditions to this emote be available to use + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// Determines conditions to this emote be unavailable to use + /// + [DataField] + public EntityWhitelist? Blacklist; + /// /// Collection of words that will be sent to chat if emote activates. /// Will be picked randomly from list. /// - [DataField("chatMessages")] + [DataField] public List ChatMessages = new(); /// @@ -32,7 +66,7 @@ public sealed partial class EmotePrototype : IPrototype /// When typed into players chat they will activate emote event. /// All words should be unique across all emote prototypes. /// - [DataField("chatTriggers")] + [DataField] public HashSet ChatTriggers = new(); } diff --git a/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs b/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs index c9a78e7d6d..2b7064c1e9 100644 --- a/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs +++ b/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs @@ -1,5 +1,6 @@ using Robust.Shared.Audio; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Chat.Prototypes; @@ -8,8 +9,8 @@ namespace Content.Shared.Chat.Prototypes; /// Sounds collection for each . /// Different entities may use different sounds collections. /// -[Prototype("emoteSounds")] -public sealed partial class EmoteSoundsPrototype : IPrototype +[Prototype("emoteSounds"), Serializable, NetSerializable] +public sealed class EmoteSoundsPrototype : IPrototype { [IdDataField] public string ID { get; private set; } = default!; diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index 886a0d5d3a..2dd671816f 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -25,6 +25,7 @@ namespace Content.Shared.Input public static readonly BoundKeyFunction CycleChatChannelBackward = "CycleChatChannelBackward"; public static readonly BoundKeyFunction EscapeContext = "EscapeContext"; public static readonly BoundKeyFunction OpenCharacterMenu = "OpenCharacterMenu"; + public static readonly BoundKeyFunction OpenEmotesMenu = "OpenEmotesMenu"; public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu"; public static readonly BoundKeyFunction OpenGuidebook = "OpenGuidebook"; public static readonly BoundKeyFunction OpenInventoryMenu = "OpenInventoryMenu"; diff --git a/Content.Server/Speech/Components/VocalComponent.cs b/Content.Shared/Speech/Components/VocalComponent.cs similarity index 83% rename from Content.Server/Speech/Components/VocalComponent.cs rename to Content.Shared/Speech/Components/VocalComponent.cs index 029d638a66..e5d2c9997f 100644 --- a/Content.Server/Speech/Components/VocalComponent.cs +++ b/Content.Shared/Speech/Components/VocalComponent.cs @@ -1,18 +1,18 @@ -using Content.Server.Speech.EntitySystems; using Content.Shared.Chat.Prototypes; using Content.Shared.Humanoid; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Server.Speech.Components; +namespace Content.Shared.Speech.Components; /// /// Component required for entities to be able to do vocal emotions. /// -[RegisterComponent] -[Access(typeof(VocalSystem))] +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] public sealed partial class VocalComponent : Component { /// @@ -20,21 +20,27 @@ public sealed partial class VocalComponent : Component /// Entities without considered to be . /// [DataField("sounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + [AutoNetworkedField] public Dictionary? Sounds; [DataField("screamId", customTypeSerializer: typeof(PrototypeIdSerializer))] + [AutoNetworkedField] public string ScreamId = "Scream"; [DataField("wilhelm")] + [AutoNetworkedField] public SoundSpecifier Wilhelm = new SoundPathSpecifier("/Audio/Voice/Human/wilhelm_scream.ogg"); [DataField("wilhelmProbability")] + [AutoNetworkedField] public float WilhelmProbability = 0.0002f; [DataField("screamAction", customTypeSerializer: typeof(PrototypeIdSerializer))] + [AutoNetworkedField] public string ScreamAction = "ActionScream"; [DataField("screamActionEntity")] + [AutoNetworkedField] public EntityUid? ScreamActionEntity; /// @@ -42,5 +48,6 @@ public sealed partial class VocalComponent : Component /// Null if no valid prototype for entity sex was found. /// [ViewVariables] + [AutoNetworkedField] public EmoteSoundsPrototype? EmoteSounds = null; } diff --git a/Content.Shared/Speech/SpeechComponent.cs b/Content.Shared/Speech/SpeechComponent.cs index 272d9ef8ca..0882120718 100644 --- a/Content.Shared/Speech/SpeechComponent.cs +++ b/Content.Shared/Speech/SpeechComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chat.Prototypes; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -26,6 +27,13 @@ namespace Content.Shared.Speech [DataField] public ProtoId SpeechVerb = "Default"; + /// + /// What emotes allowed to use event if emote is false + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public List> AllowedEmotes = new(); + /// /// A mapping from chat suffixes loc strings to speech verb prototypes that should be conditionally used. /// For things like '?' changing to 'asks' or '!!' making text bold and changing to 'yells'. Can be overridden if necessary. diff --git a/Content.Shared/Wagging/WaggingComponent.cs b/Content.Shared/Wagging/WaggingComponent.cs index 76881827dd..70e7f009c7 100644 --- a/Content.Shared/Wagging/WaggingComponent.cs +++ b/Content.Shared/Wagging/WaggingComponent.cs @@ -17,9 +17,6 @@ public sealed partial class WaggingComponent : Component [DataField] public EntityUid? ActionEntity; - [DataField] - public ProtoId EmoteId = "WagTail"; - /// /// Suffix to add to get the animated marking. /// diff --git a/Content.Shared/Whitelist/EntityWhitelist.cs b/Content.Shared/Whitelist/EntityWhitelist.cs index 942de2b0e8..b412a09b98 100644 --- a/Content.Shared/Whitelist/EntityWhitelist.cs +++ b/Content.Shared/Whitelist/EntityWhitelist.cs @@ -94,6 +94,9 @@ namespace Content.Shared.Whitelist return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags); } + if (RequireAll) + return true; + return false; } } diff --git a/Resources/Locale/en-US/HUD/game-hud.ftl b/Resources/Locale/en-US/HUD/game-hud.ftl index 7f6573d2ad..ea423f080a 100644 --- a/Resources/Locale/en-US/HUD/game-hud.ftl +++ b/Resources/Locale/en-US/HUD/game-hud.ftl @@ -1,6 +1,7 @@ game-hud-open-escape-menu-button-tooltip = Open escape menu. game-hud-open-guide-menu-button-tooltip = Open guidebook menu. game-hud-open-character-menu-button-tooltip = Open character menu. +game-hud-open-emotes-menu-button-tooltip= Open emotes menu. game-hud-open-inventory-menu-button-tooltip = Open inventory menu. game-hud-open-crafting-menu-button-tooltip = Open crafting menu. game-hud-open-actions-menu-button-tooltip = Open actions menu. diff --git a/Resources/Locale/en-US/chat/emotes.ftl b/Resources/Locale/en-US/chat/emotes.ftl new file mode 100644 index 0000000000..86d79ffe4f --- /dev/null +++ b/Resources/Locale/en-US/chat/emotes.ftl @@ -0,0 +1,60 @@ +# Names +chat-emote-name-scream = Scream +chat-emote-name-laugh = Laugh +chat-emote-name-honk = Honk +chat-emote-name-sigh = Sigh +chat-emote-name-whistle = Whistle +chat-emote-name-crying = Crying +chat-emote-name-squish = Squish +chat-emote-name-chitter = Chitter +chat-emote-name-squeak = Squeak +chat-emote-name-click = Click +chat-emote-name-clap = Clap +chat-emote-name-snap = Snap +chat-emote-name-salute = Salute +chat-emote-name-deathgasp = Deathgasp +chat-emote-name-buzz = Buzz +chat-emote-name-weh = Weh +chat-emote-name-chirp = Chirp +chat-emote-name-beep = Beep +chat-emote-name-chime = Chime +chat-emote-name-buzztwo = Buzz Two +chat-emote-name-ping = Ping +chat-emote-name-sneeze = Sneeze +chat-emote-name-cough = Cough +chat-emote-name-catmeow = Cat Meow +chat-emote-name-cathisses = Cat Hisses +chat-emote-name-monkeyscreeches = Monkey Screeches +chat-emote-name-robotbeep = Robot +chat-emote-name-yawn = Yawn +chat-emote-name-snore = Snore + +# Message +chat-emote-msg-scream = screams! +chat-emote-msg-laugh = laughs +chat-emote-msg-honk = honks +chat-emote-msg-sigh = sighs +chat-emote-msg-whistle = whistle +chat-emote-msg-crying = crying +chat-emote-msg-squish = squishing +chat-emote-msg-chitter = chitters. +chat-emote-msg-squeak = squeaks. +chat-emote-msg-click = click. +chat-emote-msg-clap = claps! +chat-emote-msg-snap = snaps fingers +chat-emote-msg-salute = salute +chat-emote-msg-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless... +chat-emote-msg-buzz = buzz! +chat-emote-msg-chirp = chirps! +chat-emote-msg-beep = beeps. +chat-emote-msg-chime = chimes. +chat-emote-msg-buzzestwo = buzzes twice. +chat-emote-msg-ping = pings. +chat-emote-msg-sneeze = sneezes +chat-emote-msg-cough = coughs +chat-emote-msg-catmeow = meows +chat-emote-msg-cathisses = hisses +chat-emote-msg-monkeyscreeches = screeches +chat-emote-msg-robotbeep = beeps +chat-emote-msg-yawn = yawns +chat-emote-msg-snore = snores diff --git a/Resources/Locale/en-US/chat/ui/emote-menu.ftl b/Resources/Locale/en-US/chat/ui/emote-menu.ftl new file mode 100644 index 0000000000..1f92a93c63 --- /dev/null +++ b/Resources/Locale/en-US/chat/ui/emote-menu.ftl @@ -0,0 +1,3 @@ +emote-menu-category-general = General +emote-menu-category-vocal = Vocal +emote-menu-category-hands = Hands diff --git a/Resources/Locale/en-US/emotes/emotes.ftl b/Resources/Locale/en-US/emotes/emotes.ftl deleted file mode 100644 index 53c12312e5..0000000000 --- a/Resources/Locale/en-US/emotes/emotes.ftl +++ /dev/null @@ -1 +0,0 @@ -emote-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless... diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 1cf56c29c6..f8218023fb 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -16,6 +16,7 @@ - type: Speech speechSounds: Squeak speechVerb: SmallMob + allowedEmotes: ['Squeak'] - type: Fixtures fixtures: fix1: @@ -432,6 +433,7 @@ - type: Speech speechVerb: Moth speechSounds: Squeak + allowedEmotes: ['Chitter'] - type: FaxableObject insertingState: inserting_mothroach - type: MothAccent @@ -817,6 +819,7 @@ - type: Speech speechVerb: Arachnid speechSounds: Arachnid + allowedEmotes: ['Click'] - type: DamageStateVisuals states: Alive: @@ -1509,6 +1512,7 @@ - type: Speech speechSounds: Squeak speechVerb: SmallMob + allowedEmotes: ['Squeak'] - type: Sprite drawdepth: SmallMobs sprite: Mobs/Animals/mouse.rsi @@ -2232,6 +2236,7 @@ - type: Speech speechVerb: Arachnid speechSounds: Arachnid + allowedEmotes: ['Click'] - type: Vocal sounds: Male: UnisexArachnid @@ -3037,6 +3042,7 @@ - type: Speech speechVerb: SmallMob speechSounds: Squeak + allowedEmotes: ['Squeak'] - type: Sprite drawdepth: SmallMobs sprite: Mobs/Animals/hamster.rsi diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml index 95c30a174f..e667b931de 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml @@ -125,6 +125,12 @@ makeSentient: true name: ghost-role-information-slimes-name description: ghost-role-information-slimes-description + - type: Speech + speechVerb: Slime + speechSounds: Slime + allowedEmotes: ['Squish'] + - type: TypingIndicator + proto: slime - type: NpcFactionMember factions: - SimpleNeutral diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml index 8cfd199dca..849cd83eba 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml @@ -263,6 +263,7 @@ - type: Speech speechVerb: Arachnid speechSounds: Arachnid + allowedEmotes: ['Click'] - type: Vocal sounds: Male: UnisexArachnid diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index d59c7bfd02..ec742e59b5 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -62,6 +62,7 @@ - type: Speech speechVerb: Arachnid speechSounds: Arachnid + allowedEmotes: ['Click'] - type: Vocal sounds: Male: UnisexArachnid diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index 199e99bef3..33bb46b172 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -22,6 +22,7 @@ accent: zombieMoth - type: Speech speechVerb: Moth + allowedEmotes: ['Chitter'] - type: TypingIndicator proto: moth - type: Butcherable diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 081973c3d2..2ab26ffcd6 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -42,6 +42,7 @@ - type: Speech speechVerb: Slime speechSounds: Slime + allowedEmotes: ['Squish'] - type: TypingIndicator proto: slime - type: Vocal diff --git a/Resources/Prototypes/Voice/disease_emotes.yml b/Resources/Prototypes/Voice/disease_emotes.yml index af93025cae..c29d6dd017 100644 --- a/Resources/Prototypes/Voice/disease_emotes.yml +++ b/Resources/Prototypes/Voice/disease_emotes.yml @@ -1,45 +1,65 @@ - type: emote id: Sneeze + name: chat-emote-name-sneeze category: Vocal - chatMessages: [sneezes] - + chatMessages: ["chat-emote-msg-sneeze"] + - type: emote id: Cough + name: chat-emote-name-cough category: Vocal - chatMessages: [coughs] + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-cough"] chatTriggers: - cough - coughs - type: emote id: CatMeow + name: chat-emote-name-catmeow category: Vocal - chatMessages: [meows] + chatMessages: ["chat-emote-msg-catmeow"] - type: emote id: CatHisses + name: chat-emote-name-cathisses category: Vocal - chatMessages: [hisses] + chatMessages: ["chat-emote-msg-cathisses"] - type: emote id: MonkeyScreeches + name: chat-emote-name-monkeyscreeches category: Vocal - chatMessages: [screeches] + chatMessages: ["chat-emote-msg-monkeyscreeches"] - type: emote id: RobotBeep + name: chat-emote-name-robotbeep category: Vocal - chatMessages: [beeps] + chatMessages: ["chat-emote-msg-robotbeep"] - type: emote id: Yawn + name: chat-emote-name-yawn category: Vocal - chatMessages: [yawns] + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-yawn"] chatTriggers: - yawn - yawns - type: emote id: Snore + name: chat-emote-name-snore category: Vocal - chatMessages: [snores] + chatMessages: ["chat-emote-msg-snore"] diff --git a/Resources/Prototypes/Voice/speech_emotes.yml b/Resources/Prototypes/Voice/speech_emotes.yml index 3b7ffc0107..a859a14c2b 100644 --- a/Resources/Prototypes/Voice/speech_emotes.yml +++ b/Resources/Prototypes/Voice/speech_emotes.yml @@ -1,8 +1,16 @@ # vocal emotes - type: emote id: Scream + name: chat-emote-name-scream category: Vocal - chatMessages: [screams!] + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-scream"] chatTriggers: - scream - screams @@ -31,8 +39,16 @@ - type: emote id: Laugh + name: chat-emote-name-laugh category: Vocal - chatMessages: [laughs] + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-laugh"] chatTriggers: - laugh - laughs @@ -64,8 +80,15 @@ - type: emote id: Honk + name: chat-emote-name-honk category: Vocal - chatMessages: [honks] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - Vocal + - BorgChassis + chatMessages: ["chat-emote-msg-honk"] chatTriggers: - honk - honk. @@ -82,8 +105,16 @@ - type: emote id: Sigh + name: chat-emote-name-sigh category: Vocal - chatMessages: [sighs] + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-sigh"] chatTriggers: - sigh - sighs @@ -94,8 +125,16 @@ - type: emote id: Whistle + name: chat-emote-name-whistle category: Vocal - chatMessages: [whistle] + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-whistle"] chatTriggers: - whistle - whistle. @@ -109,8 +148,16 @@ - type: emote id: Crying + name: chat-emote-name-crying category: Vocal - chatMessages: [crying] + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-crying"] chatTriggers: - cry - cry. @@ -132,8 +179,17 @@ - type: emote id: Squish + name: chat-emote-name-squish category: Vocal - chatMessages: [squishing] + available: false + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-squish"] chatTriggers: - squish - squish. @@ -147,8 +203,17 @@ - type: emote id: Chitter + name: chat-emote-name-chitter category: Vocal - chatMessages: [chitters.] + available: false + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-chitter"] chatTriggers: - chitter - chitter. @@ -162,8 +227,16 @@ - type: emote id: Squeak + name: chat-emote-name-squeak category: Vocal - chatMessages: [squeaks.] + available: false + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-squeak"] chatTriggers: - squeak - squeak. @@ -177,8 +250,17 @@ - type: emote id: Click + name: chat-emote-name-click category: Vocal - chatMessages: [click.] + available: false + icon: Interface/Actions/scream.png + whitelist: + components: + - Vocal + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-click"] chatTriggers: - click - click. @@ -190,8 +272,16 @@ # hand emotes - type: emote id: Clap + name: chat-emote-name-clap category: Hands - chatMessages: [claps!] + icon: Interface/Actions/scream.png + whitelist: + components: + - Hands + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-clap"] chatTriggers: - clap - claps @@ -202,8 +292,16 @@ - type: emote id: Snap + name: chat-emote-name-snap category: Hands - chatMessages: [snaps fingers] # snaps <{THEIR($ent)}> fingers? + icon: Interface/Actions/scream.png + whitelist: + components: + - Hands + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-snap"] # snaps <{THEIR($ent)}> fingers? chatTriggers: - snap - snaps @@ -221,8 +319,16 @@ - type: emote id: Salute + name: chat-emote-name-salute category: Hands - chatMessages: [Salute] + icon: Interface/Actions/scream.png + whitelist: + components: + - Hands + blacklist: + components: + - BorgChassis + chatMessages: ["chat-emote-msg-salute"] chatTriggers: - salute - salute. @@ -233,14 +339,26 @@ - type: emote id: DefaultDeathgasp - chatMessages: ["emote-deathgasp"] + name: chat-emote-name-deathgasp + icon: Interface/Actions/scream.png + whitelist: + components: + - MobState + chatMessages: ["chat-emote-msg-deathgasp"] chatTriggers: - deathgasp - type: emote id: Buzz + name: chat-emote-name-buzz category: Vocal - chatMessages: [buzz!] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - BorgChassis + - Vocal + chatMessages: ["chat-emote-msg-buzz"] chatTriggers: - buzzing - buzzing! @@ -257,13 +375,20 @@ - type: emote id: Weh + name: chat-emote-name-weh category: Vocal chatMessages: [Wehs!] - type: emote id: Chirp + name: chat-emote-name-chirp category: Vocal - chatMessages: [chirps!] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - Nymph + chatMessages: ["chat-emote-msg-chirp"] chatTriggers: - chirp - chirp! @@ -281,8 +406,15 @@ # Machine Emotes - type: emote id: Beep + name: chat-emote-name-beep category: Vocal - chatMessages: [beeps.] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - BorgChassis + - Vocal + chatMessages: ["chat-emote-msg-beep"] chatTriggers: - beep - beep! @@ -299,8 +431,15 @@ - type: emote id: Chime + name: chat-emote-name-chime category: Vocal - chatMessages: [chimes.] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - BorgChassis + - Vocal + chatMessages: ["chat-emote-msg-chime"] chatTriggers: - chime - chime. @@ -317,8 +456,15 @@ - type: emote id: Buzz-Two + name: chat-emote-name-buzztwo category: Vocal - chatMessages: [buzzesTwice.] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - BorgChassis + - Vocal + chatMessages: ["chat-emote-msg-buzzestwo"] chatTriggers: - buzztwice - buzztwice. @@ -353,8 +499,15 @@ - type: emote id: Ping + name: chat-emote-name-ping category: Vocal - chatMessages: [pings.] + icon: Interface/Actions/scream.png + whitelist: + requireAll: true + components: + - BorgChassis + - Vocal + chatMessages: ["chat-emote-msg-ping"] chatTriggers: - ping - ping. diff --git a/Resources/Prototypes/Voice/tail_emotes.yml b/Resources/Prototypes/Voice/tail_emotes.yml deleted file mode 100644 index 610a2ea801..0000000000 --- a/Resources/Prototypes/Voice/tail_emotes.yml +++ /dev/null @@ -1,14 +0,0 @@ -- type: emote - id: WagTail - chatMessages: [wags tail] - chatTriggers: - - wag - - wag. - - wags - - wags. - - wagging - - wagging. - - wag tail - - wag tail. - - wags tail - - wags tail. diff --git a/Resources/Textures/Interface/emotes.svg b/Resources/Textures/Interface/emotes.svg new file mode 100644 index 0000000000..352f7ed294 --- /dev/null +++ b/Resources/Textures/Interface/emotes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Textures/Interface/emotes.svg.192dpi.png b/Resources/Textures/Interface/emotes.svg.192dpi.png new file mode 100644 index 0000000000000000000000000000000000000000..4e1f3c4f481c99304577a04aa62dc0096b4a8bc3 GIT binary patch literal 1818 zcmV+#2j%#QP)3xFE6!aRIl84~Pc0xI|6#!AM*~G)RoT`JfL*!vkte zP!lx~ml!o_Br1v~LR>&Z6hVRn5JV8X!N6r-FP{%p!!$EpHQjT2#*6YNle+Xdb?X1S ztGlXB71CiG#sL5WfXTq{f167^0FJJ=;+>Q+A9x7pS1`Xq0ki<)fSJIIdV{<`HTD9Z z_~4!5CIDT4D}dL5tqHCh0jnqA4=KWvXb0v2PXMEUt_{!?;&ujb5AX)Cu7cMez#jki znQ8TdcLV4lX^W)pIrwarv_R6r#BZ^g?a2|Nl1Eob10)TWG*r?ENn`xABw8iCFXu42O3EdCaKx4eN66j8AsvXZN75=u%jLv=BWbRg zZI9IX#h0l%njt)f^^z9JJ^ihcc1YSTsm=dZ_}`l3)69tFzRWFrP|k+x!RKancY|WD z^yMJH%#5V5OQhn74IE?-vl-jMLv_+G--lBP(SVP@OX z9DL6?U(#KY?o);v3baVNU(z_aDQ9Rs(FS}XX^NT63;2|7Ocp&7(7ncr?A}a)4vskl zSWyvcYK6}^er1&h`+=teZBFw(59A@*9%$&*yrL5^FRLMZVNTIW+?dkd3~#HN9mc-u zy93w%%qRCCtgT@`r310k>XzQ^U6qEI@Ew8c(bhY5+?j-Rf^(0`5bUCDv$Qc1oP7DZXQY?=yDqHe53Fm_WOw@5QJ}TPy=JO9v+u z(y&d^EJ@!>`dx0Ov|my$NvFwum9LN#>LIU%R%gHvU-xXeZrmdGmEIufH%W6PePw1l z1NBX=i+7ha(9BjP?Xl1NUlCggy(Hb>e}%%#mPuOHfe?LE&fK(C8e{MH`VKVO8PMOC zZDv;Wj>m*AkJt_coaM{ZUK10(w0JH}#V}u{>i91UFFo;m!p&^Gq>YVW)Fhni%Oz>X z*b|rBFm=cQI9}x^o~ogi@b3y`)I5xmo1?5Wv(#a^mhd@+Q)&(_@#Wl*Lzb={TC8+t zT~lKEV*I+!=w}y1r0uu`@m7mnAbXUDA~S+fR~S%E4=}q&w8u&5G;f9_Y_z zR%;m?1~4eHI{LMac-?>-fDeF|>n0YC|B>hRWXvzqX#wB|x$cvO0cN(^ucpq6glU)C z%H5QR+s(}ORkYP#<*#xYrkUB^0MCAM$&Mo=onvN;BYbKDm>7BL6TQzcT>dMDUBJh{ zt+>QQDgKfE+Q+z~CLzqK!SiA7H@`!I69M}Gy8~oqC;VmveA2K6m_+rtl^CwA!LyXJ zn^=f&AN1`Ap=-R)P+(^c@p-E}K>5g3y;uiant|6$@Bdce zq7#jL79qMDxIN)J68N@G(I1lG03mR__uoYwY41G3&&GAB5ZZyq@WT~gz*Szzjv8XO z0`E|xYbUrI2`u$>C+E?T|G1-T@OeoSYPc6mdp6BZ?rrtB@9RcpcH7%s7XyHH) z+R%TMiQ0{((3kOaH=J-0>fw7I&lo`OW zl;_KZ3CAry)_ea=z~#-*lMA@)%8Nw^2TldvP53SXnsKnWegHQCt$Bs}8+RFQ_Ewtb z?*pcg@esnl4xk_K3gH9>xcBx^;91~PU`vI4CJ;+!bzb-``yAE(VJ3u4{)UZcB(J*W z>h%D3G~+zE<0Yrdoha@lX_KVC<&KifmbB2!_BV^yVfgxBvhE07*qo IM6N<$f{%!Q9smFU literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/emotes.svg.192dpi.png.yml b/Resources/Textures/Interface/emotes.svg.192dpi.png.yml new file mode 100644 index 0000000000..dabd6601f7 --- /dev/null +++ b/Resources/Textures/Interface/emotes.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index b08f4cb4ed..c11f59d17c 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -187,6 +187,9 @@ binds: - function: OpenCharacterMenu type: State key: C +- function: OpenEmotesMenu + type: State + key: Y - function: TextCursorSelect # TextCursorSelect HAS to be above ExamineEntity # So that LineEdit receives it correctly. -- 2.52.0