]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Role subtypes (#35359)
authorErrant <35878406+Errant-4@users.noreply.github.com>
Wed, 16 Apr 2025 17:04:48 +0000 (19:04 +0200)
committerGitHub <noreply@github.com>
Wed, 16 Apr 2025 17:04:48 +0000 (19:04 +0200)
20 files changed:
Content.Client/Administration/AdminNameOverlay.cs
Content.Client/Administration/OverlayOptions.cs [new file with mode: 0644]
Content.Client/Administration/Systems/AdminSystem.Overlay.cs
Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs
Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabOptions.cs [new file with mode: 0644]
Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml
Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs
Content.Client/UserInterface/Systems/Character/CharacterUIController.cs
Content.Server/Administration/Systems/AdminSystem.cs
Content.Shared/Administration/PlayerInfo.cs
Content.Shared/CCVar/CCVars.Interface.cs
Content.Shared/Mind/MindComponent.cs
Content.Shared/Mind/RoleTypePrototype.cs
Content.Shared/Roles/MindRoleComponent.cs
Content.Shared/Roles/SharedRoleSystem.cs
Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
Resources/Locale/en-US/mind/role-types.ftl
Resources/Prototypes/Roles/MindRoles/mind_roles.yml
Resources/Prototypes/Roles/role_types.yml

index c0f31f1e3d063a04111bd9e4ed2edb46899e3cf7..0d424cbff047f0bac43bce692d28b417a70838d1 100644 (file)
@@ -6,6 +6,7 @@ using Content.Shared.Administration;
 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;
@@ -22,10 +23,11 @@ internal sealed class AdminNameOverlay : Overlay
     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;
@@ -33,9 +35,10 @@ internal sealed class AdminNameOverlay : Overlay
     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(
@@ -45,20 +48,22 @@ internal sealed class AdminNameOverlay : Overlay
         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);
@@ -67,6 +72,22 @@ internal sealed class AdminNameOverlay : Overlay
         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)
@@ -183,34 +204,56 @@ internal sealed class AdminNameOverlay : Overlay
                 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));
         }
diff --git a/Content.Client/Administration/OverlayOptions.cs b/Content.Client/Administration/OverlayOptions.cs
new file mode 100644 (file)
index 0000000..3919560
--- /dev/null
@@ -0,0 +1,15 @@
+namespace Content.Client.Administration;
+
+public enum AdminOverlayAntagFormat
+{
+    Binary,
+    Roletype,
+    Subtype
+}
+
+public enum AdminOverlayAntagSymbolStyle
+{
+    Off,
+    Basic,
+    Specific
+}
index ba56f4694ff990724a5e0b2e45a0533a7335c4ce..a630df45213c4a6241d01f46dde032b6a071aea5 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Client.Administration.Managers;
+using Content.Shared.Roles;
 using Robust.Client.Graphics;
 using Robust.Client.ResourceManagement;
 using Robust.Client.UserInterface;
@@ -15,6 +16,7 @@ namespace Content.Client.Administration.Systems
         [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!;
 
@@ -30,7 +32,8 @@ namespace Content.Client.Administration.Systems
                 _resourceCache,
                 _entityLookup,
                 _userInterfaceManager,
-                _configurationManager);
+                _configurationManager,
+                _roles);
             _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
         }
 
@@ -46,7 +49,8 @@ namespace Content.Client.Administration.Systems
 
         public void AdminOverlayOn()
         {
-            if (_overlayManager.HasOverlay<AdminNameOverlay>()) return;
+            if (_overlayManager.HasOverlay<AdminNameOverlay>())
+                return;
             _overlayManager.AddOverlay(_adminNameOverlay);
             OverlayEnabled?.Invoke();
         }
index b5b2b0e345611c9f035d0c497b22e974ab973b04..2a25d9e0aabc1c7dd43b2345617c69c8d4d9abf2 100644 (file)
@@ -32,6 +32,10 @@ public sealed partial class PlayerTab : Control
     private bool _ascending = true;
     private bool _showDisconnected;
 
+    private AdminPlayerTabColorOption _playerTabColorSetting;
+    private AdminPlayerTabRoleTypeOption _playerTabRoleSetting;
+    private AdminPlayerTabSymbolOption _playerTabSymbolSetting;
+
     public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
 
     public PlayerTab()
@@ -44,9 +48,10 @@ public sealed partial class PlayerTab : Control
         _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;
@@ -113,8 +118,27 @@ public sealed partial class PlayerTab : Control
 
     #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);
     }
 
@@ -140,7 +164,12 @@ public sealed partial class PlayerTab : Control
         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}";
     }
index 939fe38fc051e288a194a63053203bf44717a31c..41e3fca76121e8d1d4b70f9787cd0445f67f2197 100644 (file)
@@ -1,42 +1,99 @@
 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;
     }
 }
diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabOptions.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabOptions.cs
new file mode 100644 (file)
index 0000000..e9eab24
--- /dev/null
@@ -0,0 +1,24 @@
+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
+}
index 449c16ad9131961c049138164369cf450707e748..ca22d7206b65ed6a2e6d82eab6199a9f9ceb2d9b 100644 (file)
@@ -6,18 +6,18 @@
             <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" />
index d2e56d80ed38fd67cd43476b49bb4586cb9f47b3..a25d83d337d677c3e25ea7a5fdc275cf1474741d 100644 (file)
@@ -1,3 +1,5 @@
+using Content.Client.Administration;
+using Content.Client.Administration.UI.Tabs.PlayerTab;
 using Content.Shared.CCVar;
 using Robust.Client.AutoGenerated;
 using Robust.Client.UserInterface;
@@ -19,12 +21,43 @@ public sealed partial class AdminOptionsTab : Control
     {
         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);
 
index e69f5774077095a052e09990f6f77700712c30b7..4fd395bbb45beae1f09ed86cb4063c3559362801 100644 (file)
@@ -221,18 +221,11 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
         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)
index 9e0664bd83f946f77253b515a530155c53cdbe4f..0868157f6ecacd21f02fa59e631c26d5ca6ffd2c 100644 (file)
@@ -235,12 +235,16 @@ public sealed class AdminSystem : EntitySystem
         // 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");
 
@@ -269,6 +273,7 @@ public sealed class AdminSystem : EntitySystem
             startingRole,
             antag,
             roleType,
+            subtype,
             sortWeight,
             GetNetEntity(session?.AttachedEntity),
             data.UserId,
index ef26ab4fca6ba6a3956d5a17005b74c0b75629ee..516442534771fcdd7a9115731421bb2b25b01a9d 100644 (file)
@@ -12,6 +12,7 @@ public sealed record PlayerInfo(
     string StartingJob,
     bool Antag,
     RoleTypePrototype RoleProto,
+    LocId? Subtype,
     int SortWeight,
     NetEntity? NetEntity,
     NetUserId SessionId,
index 85e06def615589ee5292e4b43f22900849861cca..fadf7a486298129151985d875888d8146ea3efde 100644 (file)
@@ -38,10 +38,13 @@ public sealed partial class CCVars
         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
@@ -50,34 +53,48 @@ public sealed partial class CCVars
         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
index fa48511a2a36a345919183cbee75095f868398e3..3b2b9344787e6c30dfcc9d998e3507e18990ffa1 100644 (file)
@@ -108,6 +108,12 @@ public sealed partial class MindComponent : Component
     [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.
index 8a4253a733b2f94322c2237d11f594b478596aa8..03f88ba75132110a971280283c356dcfb7ceb610 100644 (file)
@@ -21,7 +21,7 @@ public sealed partial class RoleTypePrototype : IPrototype
     ///     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.
index a52a3f5c02d7fc250ec3c1adf71ceeec55cdb098..09593c94cd6d0d88fff1cfc46b1a8aa2c6c4666d 100644 (file)
@@ -15,7 +15,7 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
     ///     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;
@@ -23,11 +23,17 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
     [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
index d6f108faaff9815ec81c825e807700a00888c23e..83ef6cbf239a4ce3fa3c922d50b73233e6e59e82 100644 (file)
@@ -6,7 +6,6 @@ using Content.Shared.Database;
 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;
@@ -202,22 +201,22 @@ public abstract class SharedRoleSystem : EntitySystem
             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>
@@ -238,21 +237,22 @@ public abstract class SharedRoleSystem : EntitySystem
         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
@@ -269,13 +269,13 @@ public abstract class SharedRoleSystem : EntitySystem
             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>
@@ -630,6 +630,14 @@ public abstract class SharedRoleSystem : EntitySystem
 
         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>
index 35ae20ec3ca908ba670a007571685a93c05d21cc..95c002b7452e14328c784d8d06875f849090d54b 100644 (file)
@@ -339,15 +339,37 @@ ui-options-censor-nudity = Censor character nudity
 
 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
index 1139bf1ab0ce4e82c641b2731748987f42c0a712..7d568fd68687c8ad97fb0452a117596b6293d4b4 100644 (file)
@@ -17,3 +17,19 @@ role-type-free-agent-color = #ffff00
 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
index cdab1c6150bef44264e42e575b6399fc9fbd9697..687b7e17bf8d90109fb1305a39cc19518eb2aaca 100644 (file)
@@ -95,7 +95,6 @@
     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
index 7ae2b44054ec17e8ef36357247c06de48b2c0a46..eb8db63977a82928c226eff646f841e14830d7e1 100644 (file)
@@ -1,5 +1,8 @@
-# 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!