Admin playerlist antag presentation rework (#35538)

* refactor(src): Minor refactor of Draw in "AdminNameOverlay. And new info about playtime player

* fix(src): Add configure classic admin owerlay

* fix

* antag status indication rework

* the cvars are free, you can just take them

* update playerlist on cvar change

* more overlay options

* tweak(src): Use _antagLabelClassic and tweak style

* tweak(src): Add config display overlay for startingJob and playTime

* tweak(src): Vector2 is replaced by var

* tweak(src): return to the end of the list

* add new option checkboxes

* passing ConfigurationManager through constructor, some format changes

* made sorting values more futureproof

* comments

* labels

* no point commenting this out when the overlay stack PR will uncomment it again anyway

* sorting prototype

* localize symbols because why not

* symmetry

* Revert "localize symbols because why not"

This reverts commit 922d4030300285a45777d62fcfd9c74b25fe7a60.

* layout and formatting stuff

* fix errant space

---------

Co-authored-by: Schrödinger <132720404+Schrodinger71@users.noreply.github.com>
This commit is contained in:
Errant
2025-03-25 17:03:59 +01:00
committed by GitHub
parent 1b74de6dbb
commit 72141abf8a
19 changed files with 199 additions and 58 deletions

View File

@@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.Administration.Systems; using Content.Client.Administration.Systems;
using Content.Client.Stylesheets;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Mind; using Content.Shared.Mind;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -14,14 +15,17 @@ namespace Content.Client.Administration;
internal sealed class AdminNameOverlay : Overlay internal sealed class AdminNameOverlay : Overlay
{ {
[Dependency] private readonly IConfigurationManager _config = default!;
private readonly AdminSystem _system; private readonly AdminSystem _system;
private readonly IEntityManager _entityManager; private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager; private readonly IEyeManager _eyeManager;
private readonly EntityLookupSystem _entityLookup; private readonly EntityLookupSystem _entityLookup;
private readonly IUserInterfaceManager _userInterfaceManager; private readonly IUserInterfaceManager _userInterfaceManager;
private readonly Font _font; private readonly Font _font;
private readonly Font _fontBold;
private bool _overlayClassic;
private bool _overlaySymbols;
private bool _overlayPlaytime;
private bool _overlayStartingJob;
//TODO make this adjustable via GUI //TODO make this adjustable via GUI
private readonly ProtoId<RoleTypePrototype>[] _filter = private readonly ProtoId<RoleTypePrototype>[] _filter =
@@ -29,17 +33,29 @@ internal sealed class AdminNameOverlay : Overlay
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic"); private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
private readonly Color _antagColorClassic = Color.OrangeRed; private readonly Color _antagColorClassic = Color.OrangeRed;
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager) public AdminNameOverlay(
AdminSystem system,
IEntityManager entityManager,
IEyeManager eyeManager,
IResourceCache resourceCache,
EntityLookupSystem entityLookup,
IUserInterfaceManager userInterfaceManager,
IConfigurationManager config)
{ {
IoCManager.InjectDependencies(this);
_system = system; _system = system;
_entityManager = entityManager; _entityManager = entityManager;
_eyeManager = eyeManager; _eyeManager = eyeManager;
_entityLookup = entityLookup; _entityLookup = entityLookup;
_userInterfaceManager = userInterfaceManager; _userInterfaceManager = userInterfaceManager;
ZIndex = 200; ZIndex = 200;
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10); // Setting this to a specific font would break the antag symbols
_font = resourceCache.NotoStack();
_fontBold = resourceCache.NotoStack(variation: "Bold");
config.OnValueChanged(CCVars.AdminOverlayClassic, (show) => { _overlayClassic = show; }, true);
config.OnValueChanged(CCVars.AdminOverlaySymbols, (show) => { _overlaySymbols = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true);
} }
public override OverlaySpace Space => OverlaySpace.ScreenSpace; public override OverlaySpace Space => OverlaySpace.ScreenSpace;
@@ -48,11 +64,6 @@ internal sealed class AdminNameOverlay : Overlay
{ {
var viewport = args.WorldAABB; var viewport = args.WorldAABB;
//TODO make this adjustable via GUI
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
var playTime = _config.GetCVar(CCVars.AdminOverlayPlaytime);
var startingJob = _config.GetCVar(CCVars.AdminOverlayStartingJob);
foreach (var playerInfo in _system.PlayerList) foreach (var playerInfo in _system.PlayerList)
{ {
var entity = _entityManager.GetEntity(playerInfo.NetEntity); var entity = _entityManager.GetEntity(playerInfo.NetEntity);
@@ -85,35 +96,51 @@ internal sealed class AdminNameOverlay : Overlay
var currentOffset = Vector2.Zero; var currentOffset = Vector2.Zero;
// Character name
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White); args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
currentOffset += lineoffset; currentOffset += lineoffset;
// Username
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White); args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
currentOffset += lineoffset; currentOffset += lineoffset;
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && playTime) // Playtime
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && _overlayPlaytime)
{ {
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? Color.Orange : Color.White); args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? Color.Orange : Color.White);
currentOffset += lineoffset; currentOffset += lineoffset;
} }
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && startingJob) // Job
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && _overlayStartingJob)
{ {
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? Color.GreenYellow : Color.White); args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? Color.GreenYellow : Color.White);
currentOffset += lineoffset; currentOffset += lineoffset;
} }
if (classic && playerInfo.Antag) // Classic Antag Label
if (_overlayClassic && playerInfo.Antag)
{ {
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, _antagLabelClassic, uiScale, Color.OrangeRed); var symbol = _overlaySymbols ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
var label = _overlaySymbols
? Loc.GetString("player-tab-character-name-antag-symbol",
("symbol", symbol),
("name", _antagLabelClassic))
: _antagLabelClassic;
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, _antagColorClassic);
currentOffset += lineoffset; currentOffset += lineoffset;
} }
else if (!classic && _filter.Contains(playerInfo.RoleProto)) // Role Type
else if (!_overlayClassic && _filter.Contains(playerInfo.RoleProto))
{ {
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper(); var symbol = _overlaySymbols && playerInfo.Antag ? playerInfo.RoleProto.Symbol : string.Empty;
var role = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var label = _overlaySymbols
? Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", role))
: role;
var color = playerInfo.RoleProto.Color; var color = playerInfo.RoleProto.Color;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, label, uiScale, color); args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
currentOffset += lineoffset; currentOffset += lineoffset;
} }
} }

View File

@@ -14,6 +14,7 @@ namespace Content.Client.Administration.Systems
[Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
private AdminNameOverlay _adminNameOverlay = default!; private AdminNameOverlay _adminNameOverlay = default!;
@@ -22,7 +23,14 @@ namespace Content.Client.Administration.Systems
private void InitializeOverlay() private void InitializeOverlay()
{ {
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup, _userInterfaceManager); _adminNameOverlay = new AdminNameOverlay(
this,
EntityManager,
_eyeManager,
_resourceCache,
_entityLookup,
_userInterfaceManager,
_configurationManager);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated; _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
} }

View File

@@ -2,11 +2,13 @@ using System.Linq;
using Content.Client.Administration.Systems; using Content.Client.Administration.Systems;
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader; using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BaseButton;
@@ -16,6 +18,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab;
public sealed partial class PlayerTab : Control public sealed partial class PlayerTab : Control
{ {
[Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IPlayerManager _playerMan = default!; [Dependency] private readonly IPlayerManager _playerMan = default!;
private const string ArrowUp = "↑"; private const string ArrowUp = "↑";
@@ -41,6 +44,10 @@ public sealed partial class PlayerTab : Control
_adminSystem.OverlayEnabled += OverlayEnabled; _adminSystem.OverlayEnabled += OverlayEnabled;
_adminSystem.OverlayDisabled += OverlayDisabled; _adminSystem.OverlayDisabled += OverlayDisabled;
_config.OnValueChanged(CCVars.AdminPlayerlistSeparateSymbols, PlayerListSettingsChanged);
_config.OnValueChanged(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerListSettingsChanged);
_config.OnValueChanged(CCVars.AdminPlayerlistRoleTypeColor, PlayerListSettingsChanged);
OverlayButton.OnPressed += OverlayButtonPressed; OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed; ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
@@ -106,6 +113,11 @@ public sealed partial class PlayerTab : Control
#region ListContainer #region ListContainer
private void PlayerListSettingsChanged(bool _)
{
RefreshPlayerList(_adminSystem.PlayerList);
}
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players) private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
{ {
_players = players; _players = players;
@@ -196,8 +208,7 @@ public sealed partial class PlayerTab : Control
Header.Username => Compare(x.Username, y.Username), Header.Username => Compare(x.Username, y.Username),
Header.Character => Compare(x.CharacterName, y.CharacterName), Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob), Header.Job => Compare(x.StartingJob, y.StartingJob),
Header.Antagonist => x.Antag.CompareTo(y.Antag), Header.RoleType => y.SortWeight - x.SortWeight,
Header.RoleType => Compare(x.RoleProto.Name , y.RoleProto.Name),
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default), Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1 _ => 1
}; };

View File

@@ -19,11 +19,6 @@
HorizontalExpand="True" HorizontalExpand="True"
ClipText="True"/> ClipText="True"/>
<customControls:VSeparator/> <customControls:VSeparator/>
<Label Name="AntagonistLabel"
SizeFlagsStretchRatio="1"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="RoleTypeLabel" <Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2" SizeFlagsStretchRatio="2"
HorizontalExpand="True" HorizontalExpand="True"

View File

@@ -1,8 +1,10 @@
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
namespace Content.Client.Administration.UI.Tabs.PlayerTab; namespace Content.Client.Administration.UI.Tabs.PlayerTab;
@@ -14,16 +16,24 @@ public sealed partial class PlayerTabEntry : PanelContainer
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat) public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
var config = IoCManager.Resolve<IConfigurationManager>();
UsernameLabel.Text = player.Username; UsernameLabel.Text = player.Username;
if (!player.Connected) if (!player.Connected)
UsernameLabel.StyleClasses.Add("Disabled"); UsernameLabel.StyleClasses.Add("Disabled");
JobLabel.Text = player.StartingJob; JobLabel.Text = player.StartingJob;
CharacterLabel.Text = player.CharacterName; var separateAntagSymbols = config.GetCVar(CCVars.AdminPlayerlistSeparateSymbols);
var genericAntagSymbol = player.Antag ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
var roleSymbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
var symbol = separateAntagSymbols ? roleSymbol : genericAntagSymbol;
CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
if (player.Antag && config.GetCVar(CCVars.AdminPlayerlistHighlightedCharacterColor))
CharacterLabel.FontColorOverride = player.RoleProto.Color;
if (player.IdentityName != player.CharacterName) if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]"; CharacterLabel.Text += $" [{player.IdentityName}]";
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name); RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
if (config.GetCVar(CCVars.AdminPlayerlistRoleTypeColor))
RoleTypeLabel.FontColorOverride = player.RoleProto.Color; RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
BackgroundColorPanel.PanelOverride = styleBoxFlat; BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString; OverallPlaytimeLabel.Text = player.PlaytimeString;

View File

@@ -25,13 +25,6 @@
Text="{Loc player-tab-job}" Text="{Loc player-tab-job}"
MouseFilter="Pass"/> MouseFilter="Pass"/>
<cc:VSeparator/> <cc:VSeparator/>
<Label Name="AntagonistLabel"
SizeFlagsStretchRatio="1"
HorizontalExpand="True"
ClipText="True"
Text="{Loc player-tab-antagonist}"
MouseFilter="Pass"/>
<cc:VSeparator/>
<Label Name="RoleTypeLabel" <Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2" SizeFlagsStretchRatio="2"
HorizontalExpand="True" HorizontalExpand="True"

View File

@@ -18,7 +18,6 @@ public sealed partial class PlayerTabHeader : Control
UsernameLabel.OnKeyBindDown += UsernameClicked; UsernameLabel.OnKeyBindDown += UsernameClicked;
CharacterLabel.OnKeyBindDown += CharacterClicked; CharacterLabel.OnKeyBindDown += CharacterClicked;
JobLabel.OnKeyBindDown += JobClicked; JobLabel.OnKeyBindDown += JobClicked;
AntagonistLabel.OnKeyBindDown += AntagonistClicked;
RoleTypeLabel.OnKeyBindDown += RoleTypeClicked; RoleTypeLabel.OnKeyBindDown += RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown += PlaytimeClicked; PlaytimeLabel.OnKeyBindDown += PlaytimeClicked;
} }
@@ -30,7 +29,6 @@ public sealed partial class PlayerTabHeader : Control
Header.Username => UsernameLabel, Header.Username => UsernameLabel,
Header.Character => CharacterLabel, Header.Character => CharacterLabel,
Header.Job => JobLabel, Header.Job => JobLabel,
Header.Antagonist => AntagonistLabel,
Header.RoleType => RoleTypeLabel, Header.RoleType => RoleTypeLabel,
Header.Playtime => PlaytimeLabel, Header.Playtime => PlaytimeLabel,
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null) _ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
@@ -42,7 +40,6 @@ public sealed partial class PlayerTabHeader : Control
UsernameLabel.Text = Loc.GetString("player-tab-username"); UsernameLabel.Text = Loc.GetString("player-tab-username");
CharacterLabel.Text = Loc.GetString("player-tab-character"); CharacterLabel.Text = Loc.GetString("player-tab-character");
JobLabel.Text = Loc.GetString("player-tab-job"); JobLabel.Text = Loc.GetString("player-tab-job");
AntagonistLabel.Text = Loc.GetString("player-tab-antagonist");
RoleTypeLabel.Text = Loc.GetString("player-tab-roletype"); RoleTypeLabel.Text = Loc.GetString("player-tab-roletype");
PlaytimeLabel.Text = Loc.GetString("player-tab-playtime"); PlaytimeLabel.Text = Loc.GetString("player-tab-playtime");
} }
@@ -73,11 +70,6 @@ public sealed partial class PlayerTabHeader : Control
HeaderClicked(args, Header.Job); HeaderClicked(args, Header.Job);
} }
private void AntagonistClicked(GUIBoundKeyEventArgs args)
{
HeaderClicked(args, Header.Antagonist);
}
private void RoleTypeClicked(GUIBoundKeyEventArgs args) private void RoleTypeClicked(GUIBoundKeyEventArgs args)
{ {
HeaderClicked(args, Header.RoleType); HeaderClicked(args, Header.RoleType);
@@ -97,7 +89,6 @@ public sealed partial class PlayerTabHeader : Control
UsernameLabel.OnKeyBindDown -= UsernameClicked; UsernameLabel.OnKeyBindDown -= UsernameClicked;
CharacterLabel.OnKeyBindDown -= CharacterClicked; CharacterLabel.OnKeyBindDown -= CharacterClicked;
JobLabel.OnKeyBindDown -= JobClicked; JobLabel.OnKeyBindDown -= JobClicked;
AntagonistLabel.OnKeyBindDown -= AntagonistClicked;
RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked; RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked; PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked;
} }
@@ -108,7 +99,6 @@ public sealed partial class PlayerTabHeader : Control
Username, Username,
Character, Character,
Job, Job,
Antagonist,
RoleType, RoleType,
Playtime Playtime
} }

View File

@@ -3,8 +3,19 @@
xmlns:ui="clr-namespace:Content.Client.Options.UI"> xmlns:ui="clr-namespace:Content.Client.Options.UI">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<ScrollContainer VerticalExpand="True" HScrollEnabled="False"> <ScrollContainer VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Orientation="Vertical" Margin="8"> <BoxContainer Orientation="Vertical" Margin="8">
<Label Text="{Loc 'ui-options-admin-player-panel'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="PlayerlistSeparateSymbolsCheckBox" Text="{Loc 'ui-options-admin-playerlist-separate-symbols'}" />
<CheckBox Name="PlayerlistCharacterColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-character-color'}" />
<CheckBox Name="PlayerlistRoleTypeColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-roletype-color'}" />
<Label Text="{Loc 'ui-options-admin-overlay-title'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="EnableClassicOverlayCheckBox" Text="{Loc 'ui-options-enable-classic-overlay'}" /> <CheckBox Name="EnableClassicOverlayCheckBox" Text="{Loc 'ui-options-enable-classic-overlay'}" />
<CheckBox Name="EnableOverlaySymbolsCheckBox" Text="{Loc 'ui-options-enable-overlay-symbols'}" />
<CheckBox Name="EnableOverlayPlaytimeCheckBox" Text="{Loc 'ui-options-enable-overlay-playtime'}" />
<CheckBox Name="EnableOverlayStartingJobCheckBox" Text="{Loc 'ui-options-enable-overlay-starting-job'}" />
</BoxContainer> </BoxContainer>
</ScrollContainer> </ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" /> <ui:OptionsTabControlRow Name="Control" Access="Public" />

View File

@@ -12,7 +12,14 @@ public sealed partial class AdminOptionsTab : Control
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistSeparateSymbols, PlayerlistSeparateSymbolsCheckBox);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerlistCharacterColorCheckBox);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistRoleTypeColor, PlayerlistRoleTypeColorCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayClassic, EnableClassicOverlayCheckBox); Control.AddOptionCheckBox(CCVars.AdminOverlayClassic, EnableClassicOverlayCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlaySymbols, EnableOverlaySymbolsCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayPlaytime, EnableOverlayPlaytimeCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayStartingJob, EnableOverlayStartingJobCheckBox);
Control.Initialize(); Control.Initialize();
} }

View File

@@ -221,6 +221,7 @@ public sealed class AdminSystem : EntitySystem
var name = data.UserName; var name = data.UserName;
var entityName = string.Empty; var entityName = string.Empty;
var identityName = string.Empty; var identityName = string.Empty;
var sortWeight = 0;
// Visible (identity) name can be different from real name // Visible (identity) name can be different from real name
if (session?.AttachedEntity != null) if (session?.AttachedEntity != null)
@@ -234,8 +235,10 @@ public sealed class AdminSystem : EntitySystem
// Starting role, antagonist status and role type // Starting role, antagonist status and role type
RoleTypePrototype roleType = new(); RoleTypePrototype roleType = new();
var startingRole = string.Empty; var startingRole = string.Empty;
if (_minds.TryGetMind(session, out var mindId, out var mindComp)) if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
{ {
sortWeight = _role.GetRoleCompByTime(mindComp)?.Comp.SortWeight ?? 0;
if (_proto.TryIndex(mindComp.RoleType, out var role)) if (_proto.TryIndex(mindComp.RoleType, out var role))
roleType = role; roleType = role;
else else
@@ -259,8 +262,19 @@ public sealed class AdminSystem : EntitySystem
overallPlaytime = playTime; overallPlaytime = playTime;
} }
return new PlayerInfo(name, entityName, identityName, startingRole, antag, roleType, GetNetEntity(session?.AttachedEntity), data.UserId, return new PlayerInfo(
connected, _roundActivePlayers.Contains(data.UserId), overallPlaytime); name,
entityName,
identityName,
startingRole,
antag,
roleType,
sortWeight,
GetNetEntity(session?.AttachedEntity),
data.UserId,
connected,
_roundActivePlayers.Contains(data.UserId),
overallPlaytime);
} }
private void OnPanicBunkerChanged(bool enabled) private void OnPanicBunkerChanged(bool enabled)

View File

@@ -12,6 +12,7 @@ public sealed record PlayerInfo(
string StartingJob, string StartingJob,
bool Antag, bool Antag,
RoleTypePrototype RoleProto, RoleTypePrototype RoleProto,
int SortWeight,
NetEntity? NetEntity, NetEntity? NetEntity,
NetUserId SessionId, NetUserId SessionId,
bool Connected, bool Connected,

View File

@@ -47,11 +47,35 @@ public sealed partial class CCVars
/// If true, the admin overlay will display the total time of the players /// If true, the admin overlay will display the total time of the players
/// </summary> /// </summary>
public static readonly CVarDef<bool> AdminOverlayPlaytime = public static readonly CVarDef<bool> AdminOverlayPlaytime =
CVarDef.Create("ui.admin_overlay_playtime", false, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_overlay_playtime", true, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <summary>
/// If true, the admin overlay will display the players starting position. /// If true, the admin overlay will display the players starting position.
/// </summary> /// </summary>
public static readonly CVarDef<bool> AdminOverlayStartingJob = public static readonly CVarDef<bool> AdminOverlayStartingJob =
CVarDef.Create("ui.admin_overlay_starting_job", false, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_overlay_starting_job", true, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If true, the admin window player tab will show different antag symbols for each role type
/// </summary>
public static readonly CVarDef<bool> AdminPlayerlistSeparateSymbols =
CVarDef.Create("ui.admin_playerlist_separate_symbols", false, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If true, characters with antag role types will have their names colored by their role type
/// </summary>
public static readonly CVarDef<bool> AdminPlayerlistHighlightedCharacterColor =
CVarDef.Create("ui.admin_playerlist_highlighted_character_color", true, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If true, the Role Types column will be colored
/// </summary>
public static readonly CVarDef<bool> AdminPlayerlistRoleTypeColor =
CVarDef.Create("ui.admin_playerlist_role_type_color", true, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If true, the admin overlay will show antag symbols
/// </summary>
public static readonly CVarDef<bool> AdminOverlaySymbols =
CVarDef.Create("ui.admin_overlay_symbols", true, CVar.CLIENTONLY | CVar.ARCHIVE);
} }

View File

@@ -22,4 +22,10 @@ public sealed partial class RoleTypePrototype : IPrototype
/// </summary> /// </summary>
[DataField] [DataField]
public Color Color { get; private set; } = Color.FromHex("#eeeeee"); public Color Color { get; private set; } = Color.FromHex("#eeeeee");
/// <summary>
/// A symbol used to represent the role type.
/// </summary>
[DataField]
public string Symbol = string.Empty;
} }

View File

@@ -45,6 +45,12 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
/// </summary> /// </summary>
[DataField] [DataField]
public ProtoId<JobPrototype>? JobPrototype { get; set; } public ProtoId<JobPrototype>? JobPrototype { get; set; }
/// <summary>
/// Used to order the characters on by role/antag status. Highest numbers are shown first.
/// </summary>
[DataField]
public int SortWeight;
} }
// Why does this base component actually exist? It does make auto-categorization easy, but before that it was useless? // Why does this base component actually exist? It does make auto-categorization easy, but before that it was useless?

View File

@@ -211,20 +211,30 @@ public abstract class SharedRoleSystem : EntitySystem
return true; return true;
} }
/// <summary>
/// Return the most recently specified role type, or Neutral
/// </summary>
private ProtoId<RoleTypePrototype> GetRoleTypeByTime(MindComponent mind) private ProtoId<RoleTypePrototype> GetRoleTypeByTime(MindComponent mind)
{ {
// If any Mind Roles specify a Role Type, return the most recent. Otherwise return Neutral var role = GetRoleCompByTime(mind);
return role?.Comp?.RoleType ?? "Neutral";
}
var roles = new List<ProtoId<RoleTypePrototype>>(); /// <summary>
/// Return the most recently specified role type's mind role entity, or null
/// </summary>
public Entity<MindRoleComponent>? GetRoleCompByTime(MindComponent mind)
{
var roles = new List<Entity<MindRoleComponent>>();
foreach (var role in mind.MindRoles) foreach (var role in mind.MindRoles)
{ {
var comp = Comp<MindRoleComponent>(role); var comp = Comp<MindRoleComponent>(role);
if (comp.RoleType is not null) if (comp.RoleType is not null)
roles.Add(comp.RoleType.Value); roles.Add((role, comp));
} }
ProtoId<RoleTypePrototype> result = (roles.Count > 0) ? roles.LastOrDefault() : "Neutral"; Entity<MindRoleComponent>? result = roles.Count > 0 ? roles.LastOrDefault() : null;
return (result); return (result);
} }

View File

@@ -2,7 +2,6 @@
player-tab-username = Username player-tab-username = Username
player-tab-character = Character player-tab-character = Character
player-tab-job = Job player-tab-job = Job
player-tab-antagonist = Antagonist
player-tab-roletype = Role Type player-tab-roletype = Role Type
player-tab-playtime = Playtime player-tab-playtime = Playtime
player-tab-show-disconnected = Show Disconnected player-tab-show-disconnected = Show Disconnected
@@ -11,3 +10,7 @@ player-tab-entry-tooltip = Playtime is displayed in days:hours:minutes.
player-tab-filter-line-edit-placeholder = Filter player-tab-filter-line-edit-placeholder = Filter
player-tab-is-antag-yes = YES player-tab-is-antag-yes = YES
player-tab-is-antag-no = NO player-tab-is-antag-no = NO
player-tab-character-name-antag-symbol = {$symbol} {$name}
player-tab-antag-prefix = 🗡

View File

@@ -337,4 +337,14 @@ ui-options-censor-nudity = Censor character nudity
## Admin menu ## Admin menu
ui-options-enable-classic-overlay = Revert antag overlay to classic mode ui-options-admin-player-panel = Admin Menu Players List
ui-options-admin-playerlist-separate-symbols = Show separate symbols for each antag role type
ui-options-admin-playerlist-character-color = Color names of antagonist characters
ui-options-admin-playerlist-roletype-color = Color role types
ui-options-admin-overlay-title = Admin Overlay
ui-options-enable-classic-overlay = Revert overlay to classic mode
ui-options-enable-overlay-symbols = Add antag symbol to text
ui-options-enable-overlay-playtime = Show playtime
ui-options-enable-overlay-starting-job = Show starting job

View File

@@ -15,6 +15,7 @@
antag: true antag: true
antagPrototype: GenericAntagonist antagPrototype: GenericAntagonist
roleType: SoloAntagonist roleType: SoloAntagonist
sortWeight: 50
#Observer #Observer
- type: entity - type: entity
@@ -23,6 +24,8 @@
name: Observer Role name: Observer Role
components: components:
- type: ObserverRole - type: ObserverRole
- type: MindRole
sortWeight: -10
#Ghost Roles #Ghost Roles
- type: entity - type: entity
@@ -48,6 +51,7 @@
- type: MindRole - type: MindRole
roleType: FreeAgent roleType: FreeAgent
antagPrototype: GenericFreeAgent antagPrototype: GenericFreeAgent
sortWeight: 30
- type: entity - type: entity
parent: MindRoleGhostRoleNeutral parent: MindRoleGhostRoleNeutral
@@ -56,6 +60,7 @@
components: components:
- type: MindRole - type: MindRole
roleType: FreeAgent roleType: FreeAgent
sortWeight: 0 # Maybe 10?
- type: entity - type: entity
parent: MindRoleGhostRoleNeutral parent: MindRoleGhostRoleNeutral
@@ -73,6 +78,7 @@
- type: MindRole - type: MindRole
roleType: SiliconAntagonist roleType: SiliconAntagonist
antagPrototype: GenericSiliconAntagonist antagPrototype: GenericSiliconAntagonist
sortWeight: 30
- type: entity - type: entity
parent: [ BaseMindRoleAntag, MindRoleGhostRoleNeutral ] parent: [ BaseMindRoleAntag, MindRoleGhostRoleNeutral ]
@@ -94,6 +100,9 @@
parent: MindRoleGhostRoleTeamAntagonist parent: MindRoleGhostRoleTeamAntagonist
id: MindRoleGhostRoleTeamAntagonistFlock id: MindRoleGhostRoleTeamAntagonistFlock
name: Ghost Role (Team Antagonist) name: Ghost Role (Team Antagonist)
components:
- type: MindRole
sortWeight: 40
# The Job MindRole holds the mob's Job prototype # The Job MindRole holds the mob's Job prototype
- type: entity - type: entity

View File

@@ -5,34 +5,40 @@
id: Neutral id: Neutral
name: role-type-crew-aligned-name name: role-type-crew-aligned-name
color: '#eeeeee' color: '#eeeeee'
symbol: "🗡" # Should never be antag, but just in case.
- type: roleType - type: roleType
id: SoloAntagonist id: SoloAntagonist
name: role-type-solo-antagonist-name name: role-type-solo-antagonist-name
color: '#d82000' color: '#d82000'
symbol: "🗡"
- type: roleType - type: roleType
id: TeamAntagonist id: TeamAntagonist
name: role-type-team-antagonist-name name: role-type-team-antagonist-name
color: '#d82000' color: '#d82000'
symbol: "⚔"
- type: roleType - type: roleType
id: FreeAgent id: FreeAgent
name: role-type-free-agent-name name: role-type-free-agent-name
color: '#ffff00' color: '#ffff00'
symbol: "☯"
- type: roleType - type: roleType
id: Familiar id: Familiar
name: role-type-familiar-name name: role-type-familiar-name
color: '#6495ed' color: '#6495ed'
symbol: "🗡" # Should never be antag, but just in case.
- type: roleType - type: roleType
id: Silicon id: Silicon
name: role-type-silicon-name name: role-type-silicon-name
color: '#6495ed' color: '#6495ed'
symbol: "🗡" # Should never be antag, but just in case.
- type: roleType - type: roleType
id: SiliconAntagonist id: SiliconAntagonist
name: role-type-silicon-antagonist-name name: role-type-silicon-antagonist-name
color: '#c832e6' color: '#c832e6'
symbol: "⛞"