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 <lunarautomaton6@gmail.com>
This commit is contained in:
Morb
2024-04-29 07:38:23 +03:00
committed by GitHub
parent 373c368b94
commit 16b3e2233a
34 changed files with 688 additions and 57 deletions

View File

@@ -0,0 +1,31 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<!-- Main -->
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-general'}" TargetLayer="General" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"/>
</ui:RadialMenuTextureButton>
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-vocal'}" TargetLayer="Vocal" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Actions/scream.png"/>
</ui:RadialMenuTextureButton>
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-hands'}" TargetLayer="Hands" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"/>
</ui:RadialMenuTextureButton>
</ui:RadialContainer>
<!-- General -->
<ui:RadialContainer Name="General" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
<!-- Vocal -->
<ui:RadialContainer Name="Vocal" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
<!-- Hands -->
<ui:RadialContainer Name="Hands" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
</ui:RadialMenu>

View File

@@ -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<ProtoId<EmotePrototype>>? OnPlayEmote;
public EmotesMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_spriteSystem = _entManager.System<SpriteSystem>();
var main = FindControl<RadialContainer>("Main");
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>();
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<SpeechComponent>(player.Value, out var speech) &&
!speech.AllowedEmotes.Contains(emote.ID))
continue;
var parent = FindControl<RadialContainer>(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<EmotePrototype> ProtoId { get; set; }
}

View File

@@ -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);

View File

@@ -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<GameplayState>
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.EmotesButton;
private EmotesMenu? _menu;
public void OnStateEntered(GameplayState state)
{
CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenEmotesMenu,
InputCmdHandler.FromDelegate(_ => ToggleEmotesMenu(false)))
.Register<EmotesUIController>();
}
public void OnStateExited(GameplayState state)
{
CommandBinds.Unregister<EmotesUIController>();
}
private void ToggleEmotesMenu(bool centered)
{
if (_menu == null)
{
// setup window
_menu = UIManager.CreateWindow<EmotesMenu>();
_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<EmotePrototype> protoId)
{
_entityManager.RaisePredictiveEvent(new PlayEmoteMessage(protoId));
}
}

View File

@@ -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<GameTopMenuBar>();
@@ -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();
}
}

View File

@@ -43,6 +43,16 @@
HorizontalExpand="True"
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
/>
<ui:MenuButton
Name="EmotesButton"
Access="Internal"
Icon="{xe:Tex '/Textures/Interface/emotes.svg.192dpi.png'}"
ToolTip="{Loc 'game-hud-open-emotes-menu-button-tooltip'}"
BoundKey = "{x:Static is:ContentKeyFunctions.OpenEmotesMenu}"
MinSize="42 64"
HorizontalExpand="True"
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
/>
<ui:MenuButton
Name="CraftingButton"
Access="Internal"

View File

@@ -1,5 +1,6 @@
using System.Collections.Frozen;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Speech;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -80,6 +81,16 @@ public partial class ChatSystem
bool ignoreActionBlocker = false
)
{
if (!(emote.Whitelist?.IsValid(source, EntityManager) ?? true))
return;
if (emote.Blacklist?.IsValid(source, EntityManager) ?? false)
return;
if (!emote.Available &&
TryComp<SpeechComponent>(source, out var speech) &&
!speech.AllowedEmotes.Contains(emote.ID))
return;
// check if proto has valid message for chat
if (emote.ChatMessages.Count != 0)
{

View File

@@ -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<PlayEmoteMessage>(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);
}
}

View File

@@ -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;

View File

@@ -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<EmotePrototype> protoId) : EntityEventArgs
{
public readonly ProtoId<EmotePrototype> ProtoId = protoId;
}

View File

@@ -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;
/// <summary>
/// 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.
/// </summary>
[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!;
/// <summary>
/// Localization string for the emote name. Displayed in the radial UI.
/// </summary>
[DataField(required: true)]
public string Name = default!;
/// <summary>
/// Determines if emote available to all by default
/// <see cref="Whitelist"/> check comes after this setting
/// <see cref="Content.Shared.Speech.SpeechComponent.AllowedEmotes"/> can ignore this setting
/// </summary>
[DataField]
public bool Available = true;
/// <summary>
/// Different emote categories may be handled by different systems.
/// Also may be used for filtering.
/// </summary>
[DataField("category")]
[DataField]
public EmoteCategory Category = EmoteCategory.General;
/// <summary>
/// An icon used to visually represent the emote in radial UI.
/// </summary>
[DataField]
public SpriteSpecifier Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/Actions/scream.png"));
/// <summary>
/// Determines conditions to this emote be available to use
/// </summary>
[DataField]
public EntityWhitelist? Whitelist;
/// <summary>
/// Determines conditions to this emote be unavailable to use
/// </summary>
[DataField]
public EntityWhitelist? Blacklist;
/// <summary>
/// Collection of words that will be sent to chat if emote activates.
/// Will be picked randomly from list.
/// </summary>
[DataField("chatMessages")]
[DataField]
public List<string> ChatMessages = new();
/// <summary>
@@ -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.
/// </summary>
[DataField("chatTriggers")]
[DataField]
public HashSet<string> ChatTriggers = new();
}

View File

@@ -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 <see cref="EmotePrototype"/>.
/// Different entities may use different sounds collections.
/// </summary>
[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!;

View File

@@ -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";

View File

@@ -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;
/// <summary>
/// Component required for entities to be able to do vocal emotions.
/// </summary>
[RegisterComponent]
[Access(typeof(VocalSystem))]
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState]
public sealed partial class VocalComponent : Component
{
/// <summary>
@@ -20,21 +20,27 @@ public sealed partial class VocalComponent : Component
/// Entities without <see cref="HumanoidComponent"/> considered to be <see cref="Sex.Unsexed"/>.
/// </summary>
[DataField("sounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer<Sex, EmoteSoundsPrototype>))]
[AutoNetworkedField]
public Dictionary<Sex, string>? Sounds;
[DataField("screamId", customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
[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<EntityPrototype>))]
[AutoNetworkedField]
public string ScreamAction = "ActionScream";
[DataField("screamActionEntity")]
[AutoNetworkedField]
public EntityUid? ScreamActionEntity;
/// <summary>
@@ -42,5 +48,6 @@ public sealed partial class VocalComponent : Component
/// Null if no valid prototype for entity sex was found.
/// </summary>
[ViewVariables]
[AutoNetworkedField]
public EmoteSoundsPrototype? EmoteSounds = null;
}

View File

@@ -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<SpeechVerbPrototype> SpeechVerb = "Default";
/// <summary>
/// What emotes allowed to use event if emote <see cref="EmotePrototype.Available"/> is false
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public List<ProtoId<EmotePrototype>> AllowedEmotes = new();
/// <summary>
/// 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.

View File

@@ -17,9 +17,6 @@ public sealed partial class WaggingComponent : Component
[DataField]
public EntityUid? ActionEntity;
[DataField]
public ProtoId<EmotePrototype> EmoteId = "WagTail";
/// <summary>
/// Suffix to add to get the animated marking.
/// </summary>

View File

@@ -94,6 +94,9 @@ namespace Content.Shared.Whitelist
return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags);
}
if (RequireAll)
return true;
return false;
}
}

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,3 @@
emote-menu-category-general = General
emote-menu-category-vocal = Vocal
emote-menu-category-hands = Hands

View File

@@ -1 +0,0 @@
emote-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless...

View File

@@ -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

View File

@@ -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

View File

@@ -263,6 +263,7 @@
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
allowedEmotes: ['Click']
- type: Vocal
sounds:
Male: UnisexArachnid

View File

@@ -62,6 +62,7 @@
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
allowedEmotes: ['Click']
- type: Vocal
sounds:
Male: UnisexArachnid

View File

@@ -22,6 +22,7 @@
accent: zombieMoth
- type: Speech
speechVerb: Moth
allowedEmotes: ['Chitter']
- type: TypingIndicator
proto: moth
- type: Butcherable

View File

@@ -42,6 +42,7 @@
- type: Speech
speechVerb: Slime
speechSounds: Slime
allowedEmotes: ['Squish']
- type: TypingIndicator
proto: slime
- type: Vocal

View File

@@ -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"]

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><g class="" transform="translate(0,0)" style=""><path d="M65.44 18.39l-2.327 9.717C53.95 66.384 49.07 107.884 49.07 151.293c0 93.415 23.097 178.085 61.047 240.014 17.218 28.096 37.652 51.6 60.447 68.92 26.69 21.257 56.353 32.962 87.377 32.962.364 0 1.147-.12 1.927-.25.623.008 1.247.02 1.87.02 60.13 0 113.67-39.724 151.62-101.653 37.95-61.93 61.047-146.598 61.047-240.014 0-41.557-4.858-81.203-13.256-118.012l-2.324-10.19-9.582 4.176c-50.92 22.196-113.98 35.705-182.086 35.713-2.014-.022-4.01-.06-6.002-.103V62.8c-1.296 0-2.586-.017-3.88-.03-69.783-2.053-125.493-18.078-182.545-40.698l-9.29-3.683zm380.816 28.747c6.792 32.774 10.824 67.647 10.824 104.156 0 90.547-22.596 172.38-58.494 230.963-35.9 58.582-84.36 93.38-136.848 93.38-.195 0-.39-.006-.584-.007v-63.987c-2.64.023-5.28-.03-7.914-.163-55.358-2.77-109.316-38.91-122.03-99.742l-2.355-11.256h94.895l37.404 14.207V80.206c1.946.042 3.896.078 5.862.098h.087c66.168 0 127.672-12.383 179.152-33.168zm-279.53 98.12c35.365 0 64.036 13.248 64.036 29.59 0 16.34-28.668 29.585-64.035 29.585-35.365 0-64.036-13.246-64.036-29.586 0-16.34 28.67-29.588 64.037-29.588zm186.282 0c-35.367 0-64.035 13.248-64.035 29.59 0 16.34 28.67 29.585 64.035 29.585 35.367 0 64.035-13.246 64.035-29.586 0-16.34-28.67-29.588-64.035-29.588zM152.572 319.17c14.72 45.053 57.247 71.428 101.602 73.646 44.8 2.24 90.238-19.45 110.416-73.646h-57.447l-44.204 16.187-42.62-16.187h-67.748z" fill="#fff" fill-opacity="1" data-darkreader-inline-fill="" style="--darkreader-inline-fill: #181a1b;"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,2 @@
sample:
filter: true

View File

@@ -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.