@@ -1,16 +1,17 @@
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 Content.Shared.Speech ;
using Content.Shared.Whitelist ;
using JetBrains.Annotations ;
using Robust.Client.Graphics ;
using Robust.Client.Input ;
using Robust.Client.Player ;
using Robust.Client.UserInterface.Controllers ;
using Robust.Client.UserInterface.Controls ;
using Robust.Shared.Input.Binding ;
using Robust.Shared.Prototypes ;
using Robust.Shared.Utility ;
namespace Content.Client.UserInterface.Systems.Emotes ;
@@ -18,11 +19,19 @@ namespace Content.Client.UserInterface.Systems.Emotes;
public sealed class EmotesUIController : UIController , IOnStateChanged < GameplayState >
{
[Dependency] private readonly IEntityManager _entityManager = default ! ;
[Dependency] private readonly IClyde _display Manager = default ! ;
[Dependency] private readonly IInput Manager _input Manager = default ! ;
[Dependency] private readonly IPrototypeManager _prototype Manager = default ! ;
[Dependency] private readonly IPlayer Manager _player Manager = default ! ;
private MenuButton ? EmotesButton = > UIManager . GetActiveUIWidgetOrNull < MenuBar . Widgets . GameTopMenuBar > ( ) ? . EmotesButton ;
private Emotes Menu? _menu ;
private SimpleRadial Menu? _menu ;
private static readonly Dictionary < EmoteCategory , ( string Tooltip , SpriteSpecifier Sprite ) > EmoteGroupingInfo
= new Dictionary < EmoteCategory , ( string Tooltip , SpriteSpecifier Sprite ) >
{
[EmoteCategory.General] = ( "emote-menu-category-general" , new SpriteSpecifier . Texture ( new ResPath ( "/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png" ) ) ) ,
[EmoteCategory.Hands] = ( "emote-menu-category-hands" , new SpriteSpecifier . Texture ( new ResPath ( "/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png" ) ) ) ,
[EmoteCategory.Vocal] = ( "emote-menu-category-vocal" , new SpriteSpecifier . Texture ( new ResPath ( "/Textures/Interface/Emotes/vocal.png" ) ) ) ,
} ;
public void OnStateEntered ( GameplayState state )
{
@@ -42,10 +51,16 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
if ( _menu = = null )
{
// setup window
_menu = UIManager . CreateWindow < EmotesMenu > ( ) ;
var prototypes = _prototypeManager . EnumeratePrototypes < EmotePrototype > ( ) ;
var models = ConvertToButtons ( prototypes ) ;
_menu = new SimpleRadialMenu ( ) ;
_menu . SetButtons ( models ) ;
_menu . Open ( ) ;
_menu . OnClose + = OnWindowClosed ;
_menu . OnOpen + = OnWindowOpen ;
_menu . OnPlayEmote + = OnPlayEmote ;
if ( EmotesButton ! = null )
EmotesButton . SetClickPressed ( true ) ;
@@ -56,16 +71,13 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
}
else
{
// Open the menu, centered on the mouse
var vpSize = _displayManager . ScreenSize ;
_menu . OpenCenteredAt ( _inputManager . MouseScreenPosition . Position / vpSize ) ;
_menu . OpenOverMouseScreenPosition ( ) ;
}
}
else
{
_menu . OnClose - = OnWindowClosed ;
_menu . OnOpen - = OnWindowOpen ;
_menu . OnPlayEmote - = OnPlayEmote ;
if ( EmotesButton ! = null )
EmotesButton . SetClickPressed ( false ) ;
@@ -118,8 +130,62 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
_menu = null ;
}
private void OnPlayEmote ( ProtoId < EmotePrototype > protoId )
private IEnumerable < RadialMenuOption > ConvertToButtons ( IEnumerable < EmotePrototype > emotePrototypes )
{
_entityManager . RaisePredictiveEvent ( new PlayEmoteMessage ( protoId ) ) ;
var whitelistSystem = EntitySystemManager . GetEntitySystem < EntityWhitelistSystem > ( ) ;
var player = _playerManager . LocalSession ? . AttachedEntity ;
Dictionary < EmoteCategory , List < RadialMenuOption > > emotesByCategory = new ( ) ;
foreach ( var emote in emotePrototypes )
{
if ( emote . Category = = EmoteCategory . Invalid )
continue ;
// only valid emotes that have ways to be triggered by chat and player have access / no restriction on
if ( emote . Category = = EmoteCategory . Invalid
| | emote . ChatTriggers . Count = = 0
| | ! ( player . HasValue & & whitelistSystem . IsWhitelistPassOrNull ( emote . Whitelist , player . Value ) )
| | whitelistSystem . IsBlacklistPass ( emote . Blacklist , player . Value ) )
continue ;
if ( ! emote . Available
& & EntityManager . TryGetComponent < SpeechComponent > ( player . Value , out var speech )
& & ! speech . AllowedEmotes . Contains ( emote . ID ) )
continue ;
if ( ! emotesByCategory . TryGetValue ( emote . Category , out var list ) )
{
list = new List < RadialMenuOption > ( ) ;
emotesByCategory . Add ( emote . Category , list ) ;
}
var actionOption = new RadialMenuActionOption < EmotePrototype > ( HandleRadialButtonClick , emote )
{
Sprite = emote . Icon ,
ToolTip = Loc . GetString ( emote . Name )
} ;
list . Add ( actionOption ) ;
}
var models = new RadialMenuOption [ emotesByCategory . Count ] ;
var i = 0 ;
foreach ( var ( key , list ) in emotesByCategory )
{
var tuple = EmoteGroupingInfo [ key ] ;
models [ i ] = new RadialMenuNestedLayerOption ( list )
{
Sprite = tuple . Sprite ,
ToolTip = Loc . GetString ( tuple . Tooltip )
} ;
i + + ;
}
return models ;
}
private void HandleRadialButtonClick ( EmotePrototype prototype )
{
_entityManager . RaisePredictiveEvent ( new PlayEmoteMessage ( prototype . ID ) ) ;
}
}