diff --git a/Content.Client/Chat/ChatBox.cs b/Content.Client/Chat/ChatBox.cs
index 87bbebba1b..7f3e1aa3ec 100644
--- a/Content.Client/Chat/ChatBox.cs
+++ b/Content.Client/Chat/ChatBox.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using Content.Shared.Chat;
+using Robust.Client.Console;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -26,6 +27,7 @@ namespace Content.Client.Chat
public Button AllButton { get; }
public Button LocalButton { get; }
public Button OOCButton { get; }
+ public Button AdminButton { get; }
///
/// Default formatting string for the ClientChatConsole.
@@ -59,6 +61,7 @@ namespace Content.Client.Chat
outerVBox.AddChild(panelContainer);
outerVBox.AddChild(hBox);
+
var contentMargin = new MarginContainer
{
MarginLeftOverride = 4, MarginRightOverride = 4,
@@ -95,6 +98,17 @@ namespace Content.Client.Chat
ToggleMode = true,
};
+ var groupController = IoCManager.Resolve();
+ if(groupController.CanCommand("asay"))
+ {
+ AdminButton = new Button
+ {
+ Text = _localize.GetString("Admin"),
+ Name = "Admin",
+ ToggleMode = true,
+ };
+ }
+
AllButton.OnToggled += OnFilterToggled;
LocalButton.OnToggled += OnFilterToggled;
OOCButton.OnToggled += OnFilterToggled;
@@ -102,6 +116,11 @@ namespace Content.Client.Chat
hBox.AddChild(AllButton);
hBox.AddChild(LocalButton);
hBox.AddChild(OOCButton);
+ if(AdminButton != null)
+ {
+ AdminButton.OnToggled += OnFilterToggled;
+ hBox.AddChild(AdminButton);
+ }
AddChild(outerVBox);
}
diff --git a/Content.Client/Chat/ChatManager.cs b/Content.Client/Chat/ChatManager.cs
index 61d1d4ae03..2264931496 100644
--- a/Content.Client/Chat/ChatManager.cs
+++ b/Content.Client/Chat/ChatManager.cs
@@ -48,6 +48,7 @@ namespace Content.Client.Chat
private const char ConCmdSlash = '/';
private const char OOCAlias = '[';
private const char MeAlias = '@';
+ private const char AdminChatAlias = ']';
private readonly List filteredHistory = new List();
@@ -55,6 +56,7 @@ namespace Content.Client.Chat
private bool _allState;
private bool _localState;
private bool _oocState;
+ private bool _adminState;
// Flag Enums for holding filtered channels
private ChatChannel _filteredChannels;
@@ -65,6 +67,7 @@ namespace Content.Client.Chat
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IEyeManager _eyeManager;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
+ [Dependency] private readonly IClientConGroupController _groupController = default!;
#pragma warning restore 649
private ChatBox _currentChatBox;
@@ -150,6 +153,8 @@ namespace Content.Client.Chat
_currentChatBox.AllButton.Pressed = !_allState;
_currentChatBox.LocalButton.Pressed = !_localState;
_currentChatBox.OOCButton.Pressed = !_oocState;
+ if(chatBox.AdminButton != null)
+ _currentChatBox.AdminButton.Pressed = !_adminState;
}
public void RemoveSpeechBubble(EntityUid entityUid, SpeechBubble bubble)
@@ -193,6 +198,9 @@ namespace Content.Client.Chat
case ChatChannel.Dead:
color = Color.MediumPurple;
break;
+ case ChatChannel.AdminChat:
+ color = Color.Red;
+ break;
}
_currentChatBox?.AddLine(messageText, message.Channel, color);
@@ -220,6 +228,18 @@ namespace Content.Client.Chat
_console.ProcessCommand($"ooc \"{CommandParsing.Escape(conInput)}\"");
break;
}
+ case AdminChatAlias:
+ {
+ var conInput = text.Substring(1);
+ if(_groupController.CanCommand("asay")){
+ _console.ProcessCommand($"asay \"{CommandParsing.Escape(conInput)}\"");
+ }
+ else
+ {
+ _console.ProcessCommand($"ooc \"{CommandParsing.Escape(conInput)}\"");
+ }
+ break;
+ }
case MeAlias:
{
var conInput = text.Substring(1);
@@ -266,10 +286,23 @@ namespace Content.Client.Chat
_filteredChannels &= ~ChatChannel.OOC;
break;
}
+ case "Admin":
+ _adminState = !_adminState;
+ if (_adminState)
+ {
+ _filteredChannels |= ChatChannel.AdminChat;
+ break;
+ }
+ else
+ {
+ _filteredChannels &= ~ChatChannel.AdminChat;
+ break;
+ }
case "ALL":
chatBox.LocalButton.Pressed ^= true;
chatBox.OOCButton.Pressed ^= true;
+ chatBox.AdminButton.Pressed ^= true;
_allState = !_allState;
break;
}
diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs
index 5e54b537dc..197774ed33 100644
--- a/Content.Client/Input/ContentContexts.cs
+++ b/Content.Client/Input/ContentContexts.cs
@@ -14,6 +14,7 @@ namespace Content.Client.Input
var common = contexts.GetContext("common");
common.AddFunction(ContentKeyFunctions.FocusChat);
common.AddFunction(ContentKeyFunctions.FocusOOC);
+ common.AddFunction(ContentKeyFunctions.FocusAdminChat);
common.AddFunction(ContentKeyFunctions.ExamineEntity);
common.AddFunction(ContentKeyFunctions.OpenTutorial);
common.AddFunction(ContentKeyFunctions.TakeScreenshot);
diff --git a/Content.Client/State/GameScreen.cs b/Content.Client/State/GameScreen.cs
index a0d947c4e8..7e36c9cca0 100644
--- a/Content.Client/State/GameScreen.cs
+++ b/Content.Client/State/GameScreen.cs
@@ -4,6 +4,7 @@ using Content.Client.Chat;
using Content.Client.Interfaces.Chat;
using Content.Client.UserInterface;
using Content.Shared.Input;
+using Robust.Client.Console;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
@@ -25,6 +26,7 @@ namespace Content.Client.State
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IChatManager _chatManager;
+ [Dependency] private readonly IClientConGroupController _groupController = default!;
#pragma warning restore 649
[ViewVariables] private ChatBox _gameChat;
@@ -34,6 +36,7 @@ namespace Content.Client.State
base.Startup();
_gameChat = new ChatBox();
+
_userInterfaceManager.StateRoot.AddChild(_gameChat);
LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10);
LayoutContainer.SetAnchorAndMarginPreset(_gameChat, LayoutContainer.LayoutPreset.TopRight, margin: 10);
@@ -50,6 +53,9 @@ namespace Content.Client.State
_inputManager.SetInputCommand(ContentKeyFunctions.FocusOOC,
InputCmdHandler.FromDelegate(s => FocusOOC(_gameChat)));
+
+ _inputManager.SetInputCommand(ContentKeyFunctions.FocusAdminChat,
+ InputCmdHandler.FromDelegate(s => FocusAdminChat(_gameChat)));
}
public override void Shutdown()
@@ -81,5 +87,17 @@ namespace Content.Client.State
chat.Input.GrabKeyboardFocus();
chat.Input.InsertAtCursor("[");
}
+
+ internal static void FocusAdminChat(ChatBox chat)
+ {
+ if (chat == null || chat.UserInterfaceManager.KeyboardFocused != null)
+ {
+ return;
+ }
+
+ chat.Input.IgnoreNext = true;
+ chat.Input.GrabKeyboardFocus();
+ chat.Input.InsertAtCursor("]");
+ }
}
}
diff --git a/Content.Client/UserInterface/TutorialWindow.cs b/Content.Client/UserInterface/TutorialWindow.cs
index d34ac4cb0d..bf6796e789 100644
--- a/Content.Client/UserInterface/TutorialWindow.cs
+++ b/Content.Client/UserInterface/TutorialWindow.cs
@@ -76,6 +76,7 @@ Open character window: [color=#a4885c]{8}[/color]
Open crafting window: [color=#a4885c]{9}[/color]
Focus chat: [color=#a4885c]{10}[/color]
Focus OOC: [color=#a4885c]{26}[/color]
+Focus Admin Chat: [color=#a4885c]{27}[/color]
Use hand/object in hand: [color=#a4885c]{22}[/color]
Do wide attack: [color=#a4885c]{23}[/color]
Use targeted entity: [color=#a4885c]{11}[/color]
@@ -112,7 +113,8 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]",
Key(WideAttack),
Key(SmartEquipBackpack),
Key(SmartEquipBelt),
- Key(FocusOOC)));
+ Key(FocusOOC),
+ Key(FocusAdminChat)));
//Gameplay
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
diff --git a/Content.Server/Chat/ChatCommands.cs b/Content.Server/Chat/ChatCommands.cs
index 99a6d06963..051c382bcf 100644
--- a/Content.Server/Chat/ChatCommands.cs
+++ b/Content.Server/Chat/ChatCommands.cs
@@ -4,6 +4,7 @@ using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Players;
using Content.Shared.GameObjects;
+using Robust.Server.Console;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Enums;
@@ -79,6 +80,19 @@ namespace Content.Server.Chat
}
}
+ internal class AdminChatCommand : IClientCommand
+ {
+ public string Command => "asay";
+ public string Description => "Send chat messages to the private admin chat channel.";
+ public string Help => "asay ";
+
+ public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
+ {
+ var chat = IoCManager.Resolve();
+ chat.SendAdminChat(player, string.Join(" ", args));
+ }
+ }
+
internal class SuicideCommand : IClientCommand
{
public string Command => "suicide";
diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs
index 60c6eb4a64..21dedca1df 100644
--- a/Content.Server/Chat/ChatManager.cs
+++ b/Content.Server/Chat/ChatManager.cs
@@ -7,6 +7,7 @@ using Content.Server.Observer;
using Content.Server.Players;
using Content.Shared.Chat;
using Content.Shared.GameObjects.EntitySystems;
+using Robust.Server.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
@@ -28,6 +29,7 @@ namespace Content.Server.Chat
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
[Dependency] private readonly IMoMMILink _mommiLink;
+ [Dependency] private readonly IConGroupController _conGroupController;
#pragma warning restore 649
public void Initialize()
@@ -112,6 +114,23 @@ namespace Content.Server.Chat
_netManager.ServerSendToMany(msg, clients.ToList());
}
+ public void SendAdminChat(IPlayerSession player, string message)
+ {
+ if(!_conGroupController.CanCommand(player, "asay"))
+ {
+ SendOOC(player, message);
+ return;
+ }
+ var clients = _playerManager.GetPlayersBy(x => _conGroupController.CanCommand(x, "asay")).Select(p => p.ConnectedClient);;
+
+ var msg = _netManager.CreateNetMessage();
+
+ msg.Channel = ChatChannel.AdminChat;
+ msg.Message = message;
+ msg.MessageWrap = $"{_localizationManager.GetString("ADMIN")}: {player.SessionId}: {{0}}";
+ _netManager.ServerSendToMany(msg, clients.ToList());
+ }
+
public void SendHookOOC(string sender, string message)
{
var msg = _netManager.CreateNetMessage();
diff --git a/Content.Server/Interfaces/Chat/IChatManager.cs b/Content.Server/Interfaces/Chat/IChatManager.cs
index 26af31827a..c3c9b6ceb3 100644
--- a/Content.Server/Interfaces/Chat/IChatManager.cs
+++ b/Content.Server/Interfaces/Chat/IChatManager.cs
@@ -18,6 +18,7 @@ namespace Content.Server.Interfaces.Chat
void EntityMe(IEntity source, string action);
void SendOOC(IPlayerSession player, string message);
+ void SendAdminChat(IPlayerSession player, string message);
void SendDeadChat(IPlayerSession player, string message);
void SendHookOOC(string sender, string message);
diff --git a/Content.Shared/Chat/ChatChannel.cs b/Content.Shared/Chat/ChatChannel.cs
index 4c9da79f0b..4b63a75b11 100644
--- a/Content.Shared/Chat/ChatChannel.cs
+++ b/Content.Shared/Chat/ChatChannel.cs
@@ -51,9 +51,14 @@ namespace Content.Shared.Chat
///
Dead = 128,
+ ///
+ /// Admin chat
+ ///
+ AdminChat = 256,
+
///
/// Unspecified.
///
- Unspecified = 256,
+ Unspecified = 512,
}
}
diff --git a/Content.Shared/Chat/MsgChatMessage.cs b/Content.Shared/Chat/MsgChatMessage.cs
index 10e73d881e..049024ccf4 100644
--- a/Content.Shared/Chat/MsgChatMessage.cs
+++ b/Content.Shared/Chat/MsgChatMessage.cs
@@ -41,7 +41,7 @@ namespace Content.Shared.Chat
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
- Channel = (ChatChannel) buffer.ReadByte();
+ Channel = (ChatChannel) buffer.ReadInt16();
Message = buffer.ReadString();
MessageWrap = buffer.ReadString();
@@ -49,6 +49,7 @@ namespace Content.Shared.Chat
{
case ChatChannel.Local:
case ChatChannel.Dead:
+ case ChatChannel.AdminChat:
case ChatChannel.Emotes:
SenderEntity = buffer.ReadEntityUid();
break;
@@ -57,7 +58,7 @@ namespace Content.Shared.Chat
public override void WriteToBuffer(NetOutgoingMessage buffer)
{
- buffer.Write((byte)Channel);
+ buffer.Write((short)Channel);
buffer.Write(Message);
buffer.Write(MessageWrap);
@@ -65,6 +66,7 @@ namespace Content.Shared.Chat
{
case ChatChannel.Local:
case ChatChannel.Dead:
+ case ChatChannel.AdminChat:
case ChatChannel.Emotes:
buffer.Write(SenderEntity);
break;
diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs
index bdbc7cee49..90b5820089 100644
--- a/Content.Shared/Input/ContentKeyFunctions.cs
+++ b/Content.Shared/Input/ContentKeyFunctions.cs
@@ -12,6 +12,7 @@ namespace Content.Shared.Input
public static readonly BoundKeyFunction ExamineEntity = "ExamineEntity";
public static readonly BoundKeyFunction FocusChat = "FocusChatWindow";
public static readonly BoundKeyFunction FocusOOC = "FocusOOCWindow";
+ public static readonly BoundKeyFunction FocusAdminChat = "FocusAdminChatWindow";
public static readonly BoundKeyFunction OpenCharacterMenu = "OpenCharacterMenu";
public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu";
public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu";
diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml
index dc76b17683..30af430579 100644
--- a/Resources/Groups/groups.yml
+++ b/Resources/Groups/groups.yml
@@ -82,6 +82,7 @@
- warp
- hostlogin
- deleteewc
+ - asay
CanViewVar: true
CanAdminPlace: true
@@ -152,6 +153,7 @@
- warp
- deleteewc
- sudo
+ - asay
CanViewVar: true
CanAdminPlace: true
CanScript: true
diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml
index 5c2a9da3c3..8a715222da 100644
--- a/Resources/keybinds.yml
+++ b/Resources/keybinds.yml
@@ -46,6 +46,9 @@ binds:
- function: FocusOOCWindow
type: State
key: LBracket
+- function: FocusAdminChatWindow
+ type: State
+ key: RBracket
- function: EditorLinePlace
type: State
key: MouseLeft