From: Morb <14136326+Morb0@users.noreply.github.com> Date: Mon, 29 Apr 2024 04:38:23 +0000 (+0300) Subject: Emotes Menu (#26702) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=16b3e2233a43ce5130d000f5ac7a57d9d2509cdd;p=space-station-14.git 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 --- 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 0000000000..4e1f3c4f48 Binary files /dev/null and b/Resources/Textures/Interface/emotes.svg.192dpi.png differ 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.