using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Content.Shared.Mind;
+using Content.Shared.Roles;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
private readonly IEyeManager _eyeManager;
private readonly EntityLookupSystem _entityLookup;
private readonly IUserInterfaceManager _userInterfaceManager;
+ private readonly SharedRoleSystem _roles;
private readonly Font _font;
private readonly Font _fontBold;
- private bool _overlayClassic;
- private bool _overlaySymbols;
+ private AdminOverlayAntagFormat _overlayFormat;
+ private AdminOverlayAntagSymbolStyle _overlaySymbolStyle;
private bool _overlayPlaytime;
private bool _overlayStartingJob;
private float _ghostFadeDistance;
private int _overlayStackMax;
private float _overlayMergeDistance;
- //TODO make this adjustable via GUI
+ //TODO make this adjustable via GUI?
private readonly ProtoId<RoleTypePrototype>[] _filter =
["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
+
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
public AdminNameOverlay(
IResourceCache resourceCache,
EntityLookupSystem entityLookup,
IUserInterfaceManager userInterfaceManager,
- IConfigurationManager config)
+ IConfigurationManager config,
+ SharedRoleSystem roles)
{
_system = system;
_entityManager = entityManager;
_eyeManager = eyeManager;
_entityLookup = entityLookup;
_userInterfaceManager = userInterfaceManager;
+ _roles = roles;
ZIndex = 200;
// Setting these to a specific ttf 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.AdminOverlayAntagFormat, (show) => { _overlayFormat = UpdateOverlayFormat(show); }, true);
+ config.OnValueChanged(CCVars.AdminOverlaySymbolStyle, (show) => { _overlaySymbolStyle = UpdateOverlaySymbolStyle(show); }, true);
config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayGhostHideDistance, (f) => { _ghostHideDistance = f; }, true);
config.OnValueChanged(CCVars.AdminOverlayMergeDistance, (f) => { _overlayMergeDistance = f; }, true);
}
+ private AdminOverlayAntagFormat UpdateOverlayFormat(string formatString)
+ {
+ if (!Enum.TryParse<AdminOverlayAntagFormat>(formatString, out var format))
+ format = AdminOverlayAntagFormat.Binary;
+
+ return format;
+ }
+
+ private AdminOverlayAntagSymbolStyle UpdateOverlaySymbolStyle(string symbolString)
+ {
+ if (!Enum.TryParse<AdminOverlayAntagSymbolStyle>(symbolString, out var symbolStyle))
+ symbolStyle = AdminOverlayAntagSymbolStyle.Off;
+
+ return symbolStyle;
+ }
+
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
protected override void Draw(in OverlayDrawArgs args)
currentOffset += lineoffset;
}
- // Classic Antag Label
- if (_overlayClassic && playerInfo.Antag)
+ // Determine antag symbol
+ string? symbol;
+ switch (_overlaySymbolStyle)
{
- 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;
- color = Color.OrangeRed;
- color.A = alpha;
- args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
- currentOffset += lineoffset;
+ case AdminOverlayAntagSymbolStyle.Specific:
+ symbol = playerInfo.RoleProto.Symbol;
+ break;
+ case AdminOverlayAntagSymbolStyle.Basic:
+ symbol = Loc.GetString("player-tab-antag-prefix");
+ break;
+ default:
+ case AdminOverlayAntagSymbolStyle.Off:
+ symbol = string.Empty;
+ break;
}
- // Role Type
- else if (!_overlayClassic && _filter.Contains(playerInfo.RoleProto))
+
+ // Determine antag/role type name
+ string? text;
+ switch (_overlayFormat)
{
- 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;
- color = playerInfo.RoleProto.Color;
- color.A = alpha;
- args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
- currentOffset += lineoffset;
+ case AdminOverlayAntagFormat.Roletype:
+ color = playerInfo.RoleProto.Color;
+ symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty;
+ text = _filter.Contains(playerInfo.RoleProto)
+ ? Loc.GetString(playerInfo.RoleProto.Name).ToUpper()
+ : string.Empty;
+ break;
+ case AdminOverlayAntagFormat.Subtype:
+ color = playerInfo.RoleProto.Color;
+ symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty;
+ text = _filter.Contains(playerInfo.RoleProto)
+ ? _roles.GetRoleSubtypeLabel(playerInfo.RoleProto.Name, playerInfo.Subtype).ToUpper()
+ : string.Empty;
+ break;
+ default:
+ case AdminOverlayAntagFormat.Binary:
+ color = Color.OrangeRed;
+ symbol = playerInfo.Antag ? symbol : string.Empty;
+ text = playerInfo.Antag ? _antagLabelClassic : string.Empty;
+ break;
}
+ // Draw antag label
+ color.A = alpha;
+ var label = !string.IsNullOrEmpty(symbol)
+ ? Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", text))
+ : text;
+ args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
+ currentOffset += lineoffset;
+
//Save the coordinates and size of the text block, for stack merge check
drawnOverlays.Add((screenCoordinatesCenter, currentOffset));
}
--- /dev/null
+namespace Content.Client.Administration;
+
+public enum AdminOverlayAntagFormat
+{
+ Binary,
+ Roletype,
+ Subtype
+}
+
+public enum AdminOverlayAntagSymbolStyle
+{
+ Off,
+ Basic,
+ Specific
+}
using Content.Client.Administration.Managers;
+using Content.Shared.Roles;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
+ [Dependency] private readonly SharedRoleSystem _roles = default!;
private AdminNameOverlay _adminNameOverlay = default!;
_resourceCache,
_entityLookup,
_userInterfaceManager,
- _configurationManager);
+ _configurationManager,
+ _roles);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
}
public void AdminOverlayOn()
{
- if (_overlayManager.HasOverlay<AdminNameOverlay>()) return;
+ if (_overlayManager.HasOverlay<AdminNameOverlay>())
+ return;
_overlayManager.AddOverlay(_adminNameOverlay);
OverlayEnabled?.Invoke();
}
private bool _ascending = true;
private bool _showDisconnected;
+ private AdminPlayerTabColorOption _playerTabColorSetting;
+ private AdminPlayerTabRoleTypeOption _playerTabRoleSetting;
+ private AdminPlayerTabSymbolOption _playerTabSymbolSetting;
+
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
public PlayerTab()
_adminSystem.OverlayEnabled += OverlayEnabled;
_adminSystem.OverlayDisabled += OverlayDisabled;
- _config.OnValueChanged(CCVars.AdminPlayerlistSeparateSymbols, PlayerListSettingsChanged);
- _config.OnValueChanged(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerListSettingsChanged);
- _config.OnValueChanged(CCVars.AdminPlayerlistRoleTypeColor, PlayerListSettingsChanged);
+ _config.OnValueChanged(CCVars.AdminPlayerTabRoleSetting, RoleSettingChanged, true);
+ _config.OnValueChanged(CCVars.AdminPlayerTabColorSetting, ColorSettingChanged, true);
+ _config.OnValueChanged(CCVars.AdminPlayerTabSymbolSetting, SymbolSettingChanged, true);
+
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
#region ListContainer
- private void PlayerListSettingsChanged(bool _)
+ private void RoleSettingChanged(string s)
+ {
+ if (!Enum.TryParse<AdminPlayerTabRoleTypeOption>(s, out var format))
+ format = AdminPlayerTabRoleTypeOption.Subtype;
+ _playerTabRoleSetting = format;
+ RefreshPlayerList(_adminSystem.PlayerList);
+ }
+
+ private void ColorSettingChanged(string s)
+ {
+ if (!Enum.TryParse<AdminPlayerTabColorOption>(s, out var format))
+ format = AdminPlayerTabColorOption.Both;
+ _playerTabColorSetting = format;
+ RefreshPlayerList(_adminSystem.PlayerList);
+ }
+
+ private void SymbolSettingChanged(string s)
{
+ if (!Enum.TryParse<AdminPlayerTabSymbolOption>(s, out var format))
+ format = AdminPlayerTabSymbolOption.Specific;
+ _playerTabSymbolSetting = format;
RefreshPlayerList(_adminSystem.PlayerList);
}
if (data is not PlayerListData { Info: var player})
return;
- var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
+ var entry = new PlayerTabEntry(
+ player,
+ new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor),
+ _playerTabColorSetting,
+ _playerTabRoleSetting,
+ _playerTabSymbolSetting);
button.AddChild(entry);
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
}
using Content.Shared.Administration;
-using Content.Shared.CCVar;
+using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Configuration;
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
[GenerateTypedNameReferences]
public sealed partial class PlayerTabEntry : PanelContainer
{
- public NetEntity? PlayerEntity;
+ [Dependency] private readonly IEntityManager _entMan = default!;
- public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
+ public PlayerTabEntry(
+ PlayerInfo player,
+ StyleBoxFlat styleBoxFlat,
+ AdminPlayerTabColorOption colorOption,
+ AdminPlayerTabRoleTypeOption roleSetting,
+ AdminPlayerTabSymbolOption symbolSetting)
{
+ IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
- var config = IoCManager.Resolve<IConfigurationManager>();
+ var roles = _entMan.System<SharedRoleSystem>();
UsernameLabel.Text = player.Username;
if (!player.Connected)
UsernameLabel.StyleClasses.Add("Disabled");
JobLabel.Text = player.StartingJob;
- 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;
+
+ var colorAntags = false;
+ var colorRoles = false;
+ switch (colorOption)
+ {
+ case AdminPlayerTabColorOption.Off:
+ break;
+ case AdminPlayerTabColorOption.Character:
+ colorAntags = true;
+ break;
+ case AdminPlayerTabColorOption.Roletype:
+ colorRoles = true;
+ break;
+ default:
+ case AdminPlayerTabColorOption.Both:
+ colorAntags = true;
+ colorRoles = true;
+ break;
+ }
+
+ var symbol = string.Empty;
+ switch (symbolSetting)
+ {
+ case AdminPlayerTabSymbolOption.Off:
+ break;
+ case AdminPlayerTabSymbolOption.Basic:
+ symbol = player.Antag ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
+ break;
+ default:
+ case AdminPlayerTabSymbolOption.Specific:
+ symbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
+ break;
+ }
+
CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
- if (player.Antag && config.GetCVar(CCVars.AdminPlayerlistHighlightedCharacterColor))
+ if (player.Antag && colorAntags)
CharacterLabel.FontColorOverride = player.RoleProto.Color;
if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]";
- RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
- if (config.GetCVar(CCVars.AdminPlayerlistRoleTypeColor))
+
+ var roletype = RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
+ var subtype = roles.GetRoleSubtypeLabel(player.RoleProto.Name, player.Subtype);
+ switch (roleSetting)
+ {
+ case AdminPlayerTabRoleTypeOption.RoleTypeSubtype:
+ RoleTypeLabel.Text = roletype != subtype
+ ? roletype + " - " +subtype
+ : roletype;
+ break;
+ case AdminPlayerTabRoleTypeOption.SubtypeRoleType:
+ RoleTypeLabel.Text = roletype != subtype
+ ? subtype + " - " + roletype
+ : roletype;
+ break;
+ case AdminPlayerTabRoleTypeOption.RoleType:
+ RoleTypeLabel.Text = roletype;
+ break;
+ default:
+ case AdminPlayerTabRoleTypeOption.Subtype:
+ RoleTypeLabel.Text = subtype;
+ break;
+ }
+
+ if (colorRoles)
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString;
- PlayerEntity = player.NetEntity;
}
}
--- /dev/null
+namespace Content.Client.Administration.UI.Tabs.PlayerTab;
+
+public enum AdminPlayerTabColorOption
+{
+ Off,
+ Character,
+ Roletype,
+ Both
+}
+
+public enum AdminPlayerTabRoleTypeOption
+{
+ RoleType,
+ Subtype,
+ RoleTypeSubtype,
+ SubtypeRoleType
+}
+
+public enum AdminPlayerTabSymbolOption
+{
+ Off,
+ Basic,
+ Specific
+}
<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'}" />
+ <ui:OptionDropDown Name="DropDownPlayerTabSymbolSetting" Title="{Loc 'ui-options-admin-player-tab-symbol-setting'}" />
+ <ui:OptionDropDown Name="DropDownPlayerTabRoleSetting" Title="{Loc 'ui-options-admin-player-tab-role-setting'}" />
+ <ui:OptionDropDown Name="DropDownPlayerTabColorSetting" Title="{Loc 'ui-options-admin-player-tab-color-setting'}" />
<Label Text="{Loc 'ui-options-admin-overlay-title'}"
StyleClasses="LabelKeyText"/>
- <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'}" />
- <ui:OptionSlider Name="OverlayMergeDistanceSlider" Title="{Loc 'ui-options-overlay-merge-distance'}"/>
- <ui:OptionSlider Name="OverlayGhostFadeSlider" Title="{Loc 'ui-options-overlay-ghost-fade-distance'}"/>
- <ui:OptionSlider Name="OverlayGhostHideSlider" Title="{Loc 'ui-options-overlay-ghost-hide-distance'}"/>
+ <ui:OptionDropDown Name="DropDownOverlayAntagFormat" Title="{Loc 'ui-options-admin-overlay-antag-format'}" />
+ <ui:OptionDropDown Name="DropDownOverlayAntagSymbol" Title="{Loc 'ui-options-admin-overlay-antag-symbol'}" />
+ <CheckBox Name="EnableOverlayPlaytimeCheckBox" Text="{Loc 'ui-options-admin-enable-overlay-playtime'}" />
+ <CheckBox Name="EnableOverlayStartingJobCheckBox" Text="{Loc 'ui-options-admin-enable-overlay-starting-job'}" />
+ <ui:OptionSlider Name="OverlayMergeDistanceSlider" Title="{Loc 'ui-options-admin-overlay-merge-distance'}"/>
+ <ui:OptionSlider Name="OverlayGhostFadeSlider" Title="{Loc 'ui-options-admin-overlay-ghost-fade-distance'}"/>
+ <ui:OptionSlider Name="OverlayGhostHideSlider" Title="{Loc 'ui-options-admin-overlay-ghost-hide-distance'}"/>
</BoxContainer>
</ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" />
+using Content.Client.Administration;
+using Content.Client.Administration.UI.Tabs.PlayerTab;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
{
RobustXamlLoader.Load(this);
- Control.AddOptionCheckBox(CCVars.AdminPlayerlistSeparateSymbols, PlayerlistSeparateSymbolsCheckBox);
- Control.AddOptionCheckBox(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerlistCharacterColorCheckBox);
- Control.AddOptionCheckBox(CCVars.AdminPlayerlistRoleTypeColor, PlayerlistRoleTypeColorCheckBox);
+ var antagFormats = new List<OptionDropDownCVar<string>.ValueOption>();
+ foreach (var format in Enum.GetValues(typeof(AdminOverlayAntagFormat)))
+ {
+ antagFormats.Add(new OptionDropDownCVar<string>.ValueOption(format.ToString()!, Loc.GetString($"ui-options-admin-overlay-antag-format-{format.ToString()!.ToLower()}")));
+ }
+
+ var antagSymbolStyles = new List<OptionDropDownCVar<string>.ValueOption>();
+ foreach (var symbol in Enum.GetValues(typeof(AdminOverlayAntagSymbolStyle)))
+ {
+ antagSymbolStyles.Add(new OptionDropDownCVar<string>.ValueOption(symbol.ToString()!, Loc.GetString($"ui-options-admin-overlay-antag-symbol-{symbol.ToString()!.ToLower()}")));
+ }
+
+ var playerTabColorSettings = new List<OptionDropDownCVar<string>.ValueOption>();
+ foreach (var setting in Enum.GetValues(typeof(AdminPlayerTabColorOption)))
+ {
+ playerTabColorSettings.Add(new OptionDropDownCVar<string>.ValueOption(setting.ToString()!, Loc.GetString($"ui-options-admin-player-tab-color-setting-{setting.ToString()!.ToLower()}")));
+ }
+
+ var playerTabRoleSettings = new List<OptionDropDownCVar<string>.ValueOption>();
+ foreach (var setting in Enum.GetValues(typeof(AdminPlayerTabRoleTypeOption)))
+ {
+ playerTabRoleSettings.Add(new OptionDropDownCVar<string>.ValueOption(setting.ToString()!, Loc.GetString($"ui-options-admin-player-tab-role-setting-{setting.ToString()!.ToLower()}")));
+ }
+
+ var playerTabSymbolSettings = new List<OptionDropDownCVar<string>.ValueOption>();
+ foreach (var setting in Enum.GetValues(typeof(AdminPlayerTabSymbolOption)))
+ {
+ playerTabSymbolSettings.Add(new OptionDropDownCVar<string>.ValueOption(setting.ToString()!, Loc.GetString($"ui-options-admin-player-tab-symbol-setting-{setting.ToString()!.ToLower()}")));
+ }
+
+ Control.AddOptionDropDown(CCVars.AdminPlayerTabSymbolSetting, DropDownPlayerTabSymbolSetting, playerTabSymbolSettings);
+ Control.AddOptionDropDown(CCVars.AdminPlayerTabRoleSetting, DropDownPlayerTabRoleSetting, playerTabRoleSettings);
+ Control.AddOptionDropDown(CCVars.AdminPlayerTabColorSetting, DropDownPlayerTabColorSetting, playerTabColorSettings);
+
+ Control.AddOptionDropDown(CCVars.AdminOverlayAntagFormat, DropDownOverlayAntagFormat, antagFormats);
+ Control.AddOptionDropDown(CCVars.AdminOverlaySymbolStyle, DropDownOverlayAntagSymbol, antagSymbolStyles);
- Control.AddOptionCheckBox(CCVars.AdminOverlayClassic, EnableClassicOverlayCheckBox);
- Control.AddOptionCheckBox(CCVars.AdminOverlaySymbols, EnableOverlaySymbolsCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayPlaytime, EnableOverlayPlaytimeCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayStartingJob, EnableOverlayStartingJobCheckBox);
if (!_ent.TryGetComponent<MindComponent>(container.Mind.Value, out var mind))
return;
- var roleText = Loc.GetString("role-type-crew-aligned-name");
- var color = Color.White;
- if (_prototypeManager.TryIndex(mind.RoleType, out var proto))
- {
- roleText = Loc.GetString(proto.Name);
- color = proto.Color;
- }
- else
- _sawmill.Error($"{_player.LocalEntity} has invalid Role Type '{mind.RoleType}'. Displaying '{roleText}' instead");
+ if (!_prototypeManager.TryIndex(mind.RoleType, out var proto))
+ _sawmill.Error($"Player '{_player.LocalSession}' has invalid Role Type '{mind.RoleType}'. Displaying default instead");
- _window.RoleType.Text = roleText;
- _window.RoleType.FontColorOverride = color;
+ _window.RoleType.Text = Loc.GetString(proto?.Name ?? "role-type-crew-aligned-name");
+ _window.RoleType.FontColorOverride = proto?.Color ?? Color.White;
}
private void CharacterDetached(EntityUid uid)
// Starting role, antagonist status and role type
RoleTypePrototype roleType = new();
var startingRole = string.Empty;
+ LocId? subtype = null;
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))
+ {
roleType = role;
+ subtype = mindComp.Subtype;
+ }
else
Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead");
startingRole,
antag,
roleType,
+ subtype,
sortWeight,
GetNetEntity(session?.AttachedEntity),
data.UserId,
string StartingJob,
bool Antag,
RoleTypePrototype RoleProto,
+ LocId? Subtype,
int SortWeight,
NetEntity? NetEntity,
NetUserId SessionId,
CVarDef.Create("outline.enabled", true, CVar.CLIENTONLY);
/// <summary>
- /// If true, the admin overlay will be displayed in the old style (showing only "ANTAG")
+ /// Determines how antagonist status/roletype is displayed. Based on AdminOverlayAntagFormats enum
+ /// Binary: Roletypes of interest get an "ANTAG" label
+ /// Roletype: Roletypes of interest will have their roletype name displayed in their specific color
+ /// Subtype: Roletypes of interest will have their subtype displayed. if subtype is not set, roletype will be shown.
/// </summary>
- public static readonly CVarDef<bool> AdminOverlayClassic =
- CVarDef.Create("ui.admin_overlay_classic", false, CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef<string> AdminOverlayAntagFormat =
+ CVarDef.Create("ui.admin_overlay_antag_format", "Subtype", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If true, the admin overlay will display the total time of the players
CVarDef.Create("ui.admin_overlay_playtime", true, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
- /// If true, the admin overlay will display the players starting position.
+ /// If true, the admin overlay will display the player's starting role.
/// </summary>
public static readonly CVarDef<bool> AdminOverlayStartingJob =
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
+ /// Determines how antagonist status/roletype is displayed Before character names on the Player Tab
+ /// Off: No symbol is shown.
+ /// Basic: The same antag symbol is shown for anyone marked as antag.
+ /// Specific: The roletype-specific symbol is shown for anyone marked as antag.
/// </summary>
- public static readonly CVarDef<bool> AdminPlayerlistSeparateSymbols =
- CVarDef.Create("ui.admin_playerlist_separate_symbols", false, CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef<string> AdminPlayerTabSymbolSetting =
+ CVarDef.Create("ui.admin_player_tab_symbols", "Specific", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
- /// If true, characters with antag role types will have their names colored by their role type
+ /// Determines what columns are colorized
+ /// Off: None.
+ /// Character: The character names of "roletypes-of-interest" have their role type's color.
+ /// Roletype: Role types are shown in their respective colors.
+ /// Both: Both characters and role types are colorized.
/// </summary>
- public static readonly CVarDef<bool> AdminPlayerlistHighlightedCharacterColor =
- CVarDef.Create("ui.admin_playerlist_highlighted_character_color", true, CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef<string> AdminPlayerTabColorSetting =
+ CVarDef.Create("ui.admin_player_tab_color", "Both", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
- /// If true, the Role Types column will be colored
+ /// Determines what's displayed in the Role column - role type, subtype, or both.
+ /// RoleType
+ /// SubType
+ /// RoleTypeSubtype
+ /// SubtypeRoleType
/// </summary>
- public static readonly CVarDef<bool> AdminPlayerlistRoleTypeColor =
- CVarDef.Create("ui.admin_playerlist_role_type_color", true, CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef<string> AdminPlayerTabRoleSetting =
+ CVarDef.Create("ui.admin_player_tab_role", "Subtype", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
- /// If true, the admin overlay will show antag symbols
+ /// Determines how antagonist status/roletype is displayed. Based on AdminOverlayAntagSymbolStyles enum
+ /// Off: No symbol is shown.
+ /// Basic: The same antag symbol is shown for anyone marked as antag.
+ /// Specific: The roletype-specific symbol is shown for anyone marked as antag.
/// </summary>
- public static readonly CVarDef<bool> AdminOverlaySymbols =
- CVarDef.Create("ui.admin_overlay_symbols", true, CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef<string> AdminOverlaySymbolStyle =
+ CVarDef.Create("ui.admin_overlay_symbol_style", "Specific", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// The range (in tiles) around the cursor within which the admin overlays of ghosts start to fade out
[DataField, AutoNetworkedField]
public ProtoId<RoleTypePrototype> RoleType = "Neutral";
+ /// <summary>
+ /// The role's subtype, shown only to admins to help with antag categorization
+ /// </summary>
+ [DataField]
+ public LocId? Subtype;
+
/// <summary>
/// The session of the player owning this mind.
/// Can be null, in which case the player is currently not logged in.
/// The role's displayed color.
/// </summary>
[DataField]
- public Color Color { get; private set; } = Color.FromHex("#eeeeee");
+ public Color Color = Color.FromHex("#eeeeee");
/// <summary>
/// A symbol used to represent the role type.
/// A single antag Mind Role is enough to make the owner mind count as Antagonist.
/// </summary>
[DataField]
- public bool Antag { get; set; } = false;
+ public bool Antag;
/// <summary>
/// The mind's current antagonist/special role, or lack thereof;
[DataField]
public ProtoId<RoleTypePrototype>? RoleType;
+ /// <summary>
+ /// The role's subtype, shown only to admins to help with antag categorization
+ /// </summary>
+ [DataField]
+ public LocId? Subtype;
+
/// <summary>
/// True if this mindrole is an exclusive antagonist. Antag setting is not checked if this is True.
/// </summary>
[DataField]
- public bool ExclusiveAntag { get; set; } = false;
+ public bool ExclusiveAntag;
/// <summary>
/// The Mind that this role belongs to
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Roles.Jobs;
-using Content.Shared.Silicons.Borgs.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
return false;
//get the most important/latest mind role
- var roleType = GetRoleTypeByTime(ent.Comp);
+ var (roleType, subtype) = GetRoleTypeByTime(ent.Comp);
- if (ent.Comp.RoleType == roleType)
+ if (ent.Comp.RoleType == roleType && ent.Comp.Subtype == subtype)
return false;
- SetRoleType(ent.Owner, roleType);
+ SetRoleType(ent.Owner, roleType, subtype);
return true;
}
/// <summary>
- /// Return the most recently specified role type, or Neutral
+ /// Return the most recently specified role type and subtype, or Neutral
/// </summary>
- private ProtoId<RoleTypePrototype> GetRoleTypeByTime(MindComponent mind)
+ private (ProtoId<RoleTypePrototype>, LocId?) GetRoleTypeByTime(MindComponent mind)
{
var role = GetRoleCompByTime(mind);
- return role?.Comp?.RoleType ?? "Neutral";
+ return (role?.Comp?.RoleType ?? "Neutral", role?.Comp?.Subtype);
}
/// <summary>
return (result);
}
- private void SetRoleType(EntityUid mind, ProtoId<RoleTypePrototype> roleTypeId)
+ private void SetRoleType(EntityUid mind, ProtoId<RoleTypePrototype> roleTypeId, LocId? subtype)
{
if (!TryComp<MindComponent>(mind, out var comp))
{
- Log.Error($"Failed to update Role Type of mind entity {ToPrettyString(mind)} to {roleTypeId}. MindComponent not found.");
+ Log.Error($"Failed to update Role Type of mind entity {ToPrettyString(mind)} to {roleTypeId}, {subtype}. MindComponent not found.");
return;
}
if (!_prototypes.HasIndex(roleTypeId))
{
- Log.Error($"Failed to change Role Type of {_minds.MindOwnerLoggingString(comp)} to {roleTypeId}. Invalid role");
+ Log.Error($"Failed to change Role Type of {_minds.MindOwnerLoggingString(comp)} to {roleTypeId}, {subtype}. Invalid role");
return;
}
comp.RoleType = roleTypeId;
+ comp.Subtype = subtype;
Dirty(mind, comp);
// Update player character window
Log.Error($"{ToPrettyString(mind)} does not have an OwnedEntity!");
_adminLogger.Add(LogType.Mind,
LogImpact.Medium,
- $"Role Type of {ToPrettyString(mind)} changed to {roleTypeId}");
+ $"Role Type of {ToPrettyString(mind)} changed to {roleTypeId}, {subtype}");
return;
}
_adminLogger.Add(LogType.Mind,
LogImpact.High,
- $"Role Type of {ToPrettyString(comp.OwnedEntity)} changed to {roleTypeId}");
+ $"Role Type of {ToPrettyString(comp.OwnedEntity)} changed to {roleTypeId}, {subtype}");
}
/// <summary>
return antag.Requirements;
}
+
+ /// <summary>
+ /// Returns the localized name of a role type's subtype. If the provided subtype parameter turns out to be empty, it returns the localized name of the role type instead.
+ /// </summary>
+ public string GetRoleSubtypeLabel(LocId roleType, LocId? subtype)
+ {
+ return string.IsNullOrEmpty(subtype) ? Loc.GetString(roleType) : Loc.GetString(subtype);
+ }
}
/// <summary>
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-player-tab-symbol-setting = Character column antag symbols
+ui-options-admin-player-tab-symbol-setting-off = No antag symbol
+ui-options-admin-player-tab-symbol-setting-basic = Show standard antag symbol
+ui-options-admin-player-tab-symbol-setting-specific = Show specific antag symbol
+
+ui-options-admin-player-tab-role-setting = Role display settings
+ui-options-admin-player-tab-role-setting-roletype = Show role type
+ui-options-admin-player-tab-role-setting-subtype = Show subtype
+ui-options-admin-player-tab-role-setting-roletypesubtype = Show role type and subtype
+ui-options-admin-player-tab-role-setting-subtyperoletype = Show subtype and role type
+
+ui-options-admin-player-tab-color-setting = Color settings
+ui-options-admin-player-tab-color-setting-off = I hate colors
+ui-options-admin-player-tab-color-setting-character = Colorize antag character names
+ui-options-admin-player-tab-color-setting-roletype = Colorize all role types
+ui-options-admin-player-tab-color-setting-both = Colorize both
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
-ui-options-overlay-merge-distance = Stack merge distance
-ui-options-overlay-ghost-fade-distance = Ghost overlay fade range from mouse
-ui-options-overlay-ghost-hide-distance = Ghost overlay hide range from mouse
+
+ui-options-admin-overlay-antag-format = Antag label style
+ui-options-admin-overlay-antag-format-binary = Show antag status
+ui-options-admin-overlay-antag-format-roletype = Show role type
+ui-options-admin-overlay-antag-format-subtype = Show subtype
+
+ui-options-admin-overlay-antag-symbol = Antag symbol style
+ui-options-admin-overlay-antag-symbol-off = No antag symbol
+ui-options-admin-overlay-antag-symbol-basic = Show standard antag symbol
+ui-options-admin-overlay-antag-symbol-specific = Show specific antag symbol
+
+ui-options-admin-enable-overlay-playtime = Show playtime
+ui-options-admin-enable-overlay-starting-job = Show starting job
+ui-options-admin-overlay-merge-distance = Stack merge distance
+ui-options-admin-overlay-ghost-fade-distance = Ghost overlay fade range from mouse
+ui-options-admin-overlay-ghost-hide-distance = Ghost overlay hide range from mouse
role-type-familiar-color = #6495ed
role-type-silicon-color = #6495ed
role-type-silicon-antagonist-color =#c832e6
+
+# Ideally, subtype names should be short
+role-subtype-traitor = Traitor
+role-subtype-thief = Thief
+role-subtype-ninja = Ninja
+role-subtype-nukie = Nukie
+role-subtype-traitor-reinforcement = Reinforcement
+role-subtype-revolutionary = Rev
+role-subtype-head-revolutionary = Head Rev
+role-subtype-initial-infected = Infected
+role-subtype-zombie = Zombie
+role-subtype-dragon = Dragon
+role-subtype-survivor = Survivor
+role-subtype-subverted = Subverted
+role-subtype-paradox-clone = Paradox
+role-subtype-wizard = Wizard
antagPrototype: GenericTeamAntagonist
# This should be used (or inherited) for team antags that are summoned or converted in large quantities, and are "secondary" to other antags
-# TODO: sort weight
- type: entity
parent: MindRoleGhostRoleTeamAntagonist
id: MindRoleGhostRoleTeamAntagonistFlock
- type: MindRole
antagPrototype: SubvertedSilicon
roleType: SiliconAntagonist
+ subtype: role-subtype-subverted
- type: SubvertedSiliconRole
# Dragon
- type: MindRole
antagPrototype: Dragon
roleType: TeamAntagonist
+ subtype: role-subtype-dragon
exclusiveAntag: true
- type: DragonRole
- type: RoleBriefing
components:
- type: MindRole
antagPrototype: SpaceNinja
+ subtype: role-subtype-ninja
exclusiveAntag: true
- type: NinjaRole
components:
- type: MindRole
antagPrototype: ParadoxClone
- roleType: SoloAntagonist
+ subtype: role-subtype-paradox-clone
- type: ParadoxCloneRole
# Nukies
components:
- type: MindRole
roleType: TeamAntagonist
+ subtype: role-subtype-nukie
exclusiveAntag: true
antagPrototype: Nukeops
- type: NukeopsRole
antagPrototype: HeadRev
exclusiveAntag: true
roleType: TeamAntagonist
+ subtype: role-subtype-head-revolutionary
- type: RevolutionaryRole
- type: entity
components:
- type: MindRole
antagPrototype: Rev
+ subtype: role-subtype-revolutionary
# Survivors (Wizard)
- type: entity
- type: MindRole
antagPrototype: Survivor
roleType: FreeAgent
+ subtype: role-subtype-survivor
- type: SurvivorRole
# Thief
components:
- type: MindRole
antagPrototype: Thief
+ subtype: role-subtype-thief
- type: ThiefRole
# Traitors
- type: MindRole
antagPrototype: Traitor
exclusiveAntag: true
+ subtype: role-subtype-traitor
- type: TraitorRole
- type: entity
components:
- type: MindRole
roleType: TeamAntagonist
+ subtype: role-subtype-traitor-reinforcement
antagPrototype: GenericTeamAntagonist
# Wizards
- type: MindRole
antagPrototype: Wizard
exclusiveAntag: true
+ subtype: role-subtype-wizard
- type: WizardRole
# Zombie Squad
antagPrototype: InitialInfected
exclusiveAntag: true
roleType: TeamAntagonist
+ subtype: role-subtype-initial-infected
- type: InitialInfectedRole
- type: entity
- type: MindRole
antagPrototype: Zombie
exclusiveAntag: true
- roleType: TeamAntagonist
+ subtype: role-subtype-zombie
- type: ZombieRole
-# For use by Role Types
-# Do not touch these
+# The primary role types are referenced by RP Rules, and were specified based on head admin input.
+# Do not create new ones without discussion.
+# You probably want a Subtype instead, anyway. Use MindRoleComponent.Subtype
+
+# Any new primary role types must be listed in RoleTypes.xml
# If you change/add a color here, also change it in role-types.ftl!