--- /dev/null
+<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>
--- /dev/null
+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; }
+}
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);
--- /dev/null
+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));
+ }
+}
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;
[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>();
_ahelp.UnloadButton();
_action.UnloadButton();
_sandbox.UnloadButton();
+ _emotes.UnloadButton();
}
public void LoadButtons()
_ahelp.LoadButton();
_action.LoadButton();
_sandbox.LoadButton();
+ _emotes.LoadButton();
}
}
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"
using System.Collections.Frozen;
using Content.Shared.Chat.Prototypes;
+using Content.Shared.Speech;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
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)
{
--- /dev/null
+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);
+ }
+}
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;
--- /dev/null
+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;
+}
+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
[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>
/// 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();
}
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;
/// 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!;
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";
-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>
/// 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>
/// Null if no valid prototype for entity sex was found.
/// </summary>
[ViewVariables]
+ [AutoNetworkedField]
public EmoteSoundsPrototype? EmoteSounds = null;
}
+using Content.Shared.Chat.Prototypes;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
[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.
[DataField]
public EntityUid? ActionEntity;
- [DataField]
- public ProtoId<EmotePrototype> EmoteId = "WagTail";
-
/// <summary>
/// Suffix to add to get the animated marking.
/// </summary>
return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags);
}
+ if (RequireAll)
+ return true;
+
return false;
}
}
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.
--- /dev/null
+# 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
--- /dev/null
+emote-menu-category-general = General
+emote-menu-category-vocal = Vocal
+emote-menu-category-hands = Hands
+++ /dev/null
-emote-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless...
- type: Speech
speechSounds: Squeak
speechVerb: SmallMob
+ allowedEmotes: ['Squeak']
- type: Fixtures
fixtures:
fix1:
- type: Speech
speechVerb: Moth
speechSounds: Squeak
+ allowedEmotes: ['Chitter']
- type: FaxableObject
insertingState: inserting_mothroach
- type: MothAccent
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
+ allowedEmotes: ['Click']
- type: DamageStateVisuals
states:
Alive:
- type: Speech
speechSounds: Squeak
speechVerb: SmallMob
+ allowedEmotes: ['Squeak']
- type: Sprite
drawdepth: SmallMobs
sprite: Mobs/Animals/mouse.rsi
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
+ allowedEmotes: ['Click']
- type: Vocal
sounds:
Male: UnisexArachnid
- type: Speech
speechVerb: SmallMob
speechSounds: Squeak
+ allowedEmotes: ['Squeak']
- type: Sprite
drawdepth: SmallMobs
sprite: Mobs/Animals/hamster.rsi
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
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
+ allowedEmotes: ['Click']
- type: Vocal
sounds:
Male: UnisexArachnid
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
+ allowedEmotes: ['Click']
- type: Vocal
sounds:
Male: UnisexArachnid
accent: zombieMoth
- type: Speech
speechVerb: Moth
+ allowedEmotes: ['Chitter']
- type: TypingIndicator
proto: moth
- type: Butcherable
- type: Speech
speechVerb: Slime
speechSounds: Slime
+ allowedEmotes: ['Squish']
- type: TypingIndicator
proto: slime
- type: Vocal
- 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"]
# 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
- 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
- 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.
- 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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
# 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
- 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
- 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.
- 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!
- 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!
# 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!
- 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.
- 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.
- 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.
+++ /dev/null
-- type: emote
- id: WagTail
- chatMessages: [wags tail]
- chatTriggers:
- - wag
- - wag.
- - wags
- - wags.
- - wagging
- - wagging.
- - wag tail
- - wag tail.
- - wags tail
- - wags tail.
--- /dev/null
+<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>
\ No newline at end of file
--- /dev/null
+sample:
+ filter: true
- 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.