Role subtypes (#35359)

This commit is contained in:
Errant
2025-04-16 19:04:48 +02:00
committed by GitHub
parent 7aa0c7562e
commit fb388d2265
20 changed files with 414 additions and 121 deletions

View File

@@ -6,6 +6,7 @@ using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -22,10 +23,11 @@ internal sealed class AdminNameOverlay : Overlay
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 SharedRoleSystem _roles;
private readonly Font _font; private readonly Font _font;
private readonly Font _fontBold; private readonly Font _fontBold;
private bool _overlayClassic; private AdminOverlayAntagFormat _overlayFormat;
private bool _overlaySymbols; private AdminOverlayAntagSymbolStyle _overlaySymbolStyle;
private bool _overlayPlaytime; private bool _overlayPlaytime;
private bool _overlayStartingJob; private bool _overlayStartingJob;
private float _ghostFadeDistance; private float _ghostFadeDistance;
@@ -33,9 +35,10 @@ internal sealed class AdminNameOverlay : Overlay
private int _overlayStackMax; private int _overlayStackMax;
private float _overlayMergeDistance; private float _overlayMergeDistance;
//TODO make this adjustable via GUI //TODO make this adjustable via GUI?
private readonly ProtoId<RoleTypePrototype>[] _filter = private readonly ProtoId<RoleTypePrototype>[] _filter =
["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"]; ["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic"); private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
public AdminNameOverlay( public AdminNameOverlay(
@@ -45,20 +48,22 @@ internal sealed class AdminNameOverlay : Overlay
IResourceCache resourceCache, IResourceCache resourceCache,
EntityLookupSystem entityLookup, EntityLookupSystem entityLookup,
IUserInterfaceManager userInterfaceManager, IUserInterfaceManager userInterfaceManager,
IConfigurationManager config) IConfigurationManager config,
SharedRoleSystem roles)
{ {
_system = system; _system = system;
_entityManager = entityManager; _entityManager = entityManager;
_eyeManager = eyeManager; _eyeManager = eyeManager;
_entityLookup = entityLookup; _entityLookup = entityLookup;
_userInterfaceManager = userInterfaceManager; _userInterfaceManager = userInterfaceManager;
_roles = roles;
ZIndex = 200; ZIndex = 200;
// Setting these to a specific ttf would break the antag symbols // Setting these to a specific ttf would break the antag symbols
_font = resourceCache.NotoStack(); _font = resourceCache.NotoStack();
_fontBold = resourceCache.NotoStack(variation: "Bold"); _fontBold = resourceCache.NotoStack(variation: "Bold");
config.OnValueChanged(CCVars.AdminOverlayClassic, (show) => { _overlayClassic = show; }, true); config.OnValueChanged(CCVars.AdminOverlayAntagFormat, (show) => { _overlayFormat = UpdateOverlayFormat(show); }, true);
config.OnValueChanged(CCVars.AdminOverlaySymbols, (show) => { _overlaySymbols = show; }, true); config.OnValueChanged(CCVars.AdminOverlaySymbolStyle, (show) => { _overlaySymbolStyle = UpdateOverlaySymbolStyle(show); }, true);
config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true); config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true); config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayGhostHideDistance, (f) => { _ghostHideDistance = f; }, 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); 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; public override OverlaySpace Space => OverlaySpace.ScreenSpace;
protected override void Draw(in OverlayDrawArgs args) protected override void Draw(in OverlayDrawArgs args)
@@ -183,33 +204,55 @@ internal sealed class AdminNameOverlay : Overlay
currentOffset += lineoffset; currentOffset += lineoffset;
} }
// Classic Antag Label // Determine antag symbol
if (_overlayClassic && playerInfo.Antag) string? symbol;
switch (_overlaySymbolStyle)
{ {
var symbol = _overlaySymbols ? Loc.GetString("player-tab-antag-prefix") : string.Empty; case AdminOverlayAntagSymbolStyle.Specific:
var label = _overlaySymbols symbol = playerInfo.RoleProto.Symbol;
? Loc.GetString("player-tab-character-name-antag-symbol", break;
("symbol", symbol), case AdminOverlayAntagSymbolStyle.Basic:
("name", _antagLabelClassic)) symbol = Loc.GetString("player-tab-antag-prefix");
: _antagLabelClassic; break;
color = Color.OrangeRed; default:
color.A = alpha; case AdminOverlayAntagSymbolStyle.Off:
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color); symbol = string.Empty;
currentOffset += lineoffset; 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; case AdminOverlayAntagFormat.Roletype:
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 = 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; 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); args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
currentOffset += lineoffset; currentOffset += lineoffset;
}
//Save the coordinates and size of the text block, for stack merge check //Save the coordinates and size of the text block, for stack merge check
drawnOverlays.Add((screenCoordinatesCenter, currentOffset)); drawnOverlays.Add((screenCoordinatesCenter, currentOffset));

View File

@@ -0,0 +1,15 @@
namespace Content.Client.Administration;
public enum AdminOverlayAntagFormat
{
Binary,
Roletype,
Subtype
}
public enum AdminOverlayAntagSymbolStyle
{
Off,
Basic,
Specific
}

View File

@@ -1,4 +1,5 @@
using Content.Client.Administration.Managers; using Content.Client.Administration.Managers;
using Content.Shared.Roles;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -15,6 +16,7 @@ namespace Content.Client.Administration.Systems
[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!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
private AdminNameOverlay _adminNameOverlay = default!; private AdminNameOverlay _adminNameOverlay = default!;
@@ -30,7 +32,8 @@ namespace Content.Client.Administration.Systems
_resourceCache, _resourceCache,
_entityLookup, _entityLookup,
_userInterfaceManager, _userInterfaceManager,
_configurationManager); _configurationManager,
_roles);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated; _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
} }
@@ -46,7 +49,8 @@ namespace Content.Client.Administration.Systems
public void AdminOverlayOn() public void AdminOverlayOn()
{ {
if (_overlayManager.HasOverlay<AdminNameOverlay>()) return; if (_overlayManager.HasOverlay<AdminNameOverlay>())
return;
_overlayManager.AddOverlay(_adminNameOverlay); _overlayManager.AddOverlay(_adminNameOverlay);
OverlayEnabled?.Invoke(); OverlayEnabled?.Invoke();
} }

View File

@@ -32,6 +32,10 @@ public sealed partial class PlayerTab : Control
private bool _ascending = true; private bool _ascending = true;
private bool _showDisconnected; private bool _showDisconnected;
private AdminPlayerTabColorOption _playerTabColorSetting;
private AdminPlayerTabRoleTypeOption _playerTabRoleSetting;
private AdminPlayerTabSymbolOption _playerTabSymbolSetting;
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown; public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
public PlayerTab() public PlayerTab()
@@ -44,9 +48,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.AdminPlayerTabRoleSetting, RoleSettingChanged, true);
_config.OnValueChanged(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerListSettingsChanged); _config.OnValueChanged(CCVars.AdminPlayerTabColorSetting, ColorSettingChanged, true);
_config.OnValueChanged(CCVars.AdminPlayerlistRoleTypeColor, PlayerListSettingsChanged); _config.OnValueChanged(CCVars.AdminPlayerTabSymbolSetting, SymbolSettingChanged, true);
OverlayButton.OnPressed += OverlayButtonPressed; OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed; ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
@@ -113,8 +118,27 @@ public sealed partial class PlayerTab : Control
#region ListContainer #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); RefreshPlayerList(_adminSystem.PlayerList);
} }
@@ -140,7 +164,12 @@ public sealed partial class PlayerTab : Control
if (data is not PlayerListData { Info: var player}) if (data is not PlayerListData { Info: var player})
return; 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.AddChild(entry);
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}"; button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
} }

View File

@@ -1,42 +1,99 @@
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.Roles;
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;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class PlayerTabEntry : PanelContainer 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); RobustXamlLoader.Load(this);
var config = IoCManager.Resolve<IConfigurationManager>(); var roles = _entMan.System<SharedRoleSystem>();
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;
var separateAntagSymbols = config.GetCVar(CCVars.AdminPlayerlistSeparateSymbols);
var genericAntagSymbol = player.Antag ? Loc.GetString("player-tab-antag-prefix") : string.Empty; var colorAntags = false;
var roleSymbol = player.Antag ? player.RoleProto.Symbol : string.Empty; var colorRoles = false;
var symbol = separateAntagSymbols ? roleSymbol : genericAntagSymbol; 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)); 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; CharacterLabel.FontColorOverride = player.RoleProto.Color;
if (player.IdentityName != player.CharacterName) if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]"; 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; RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
BackgroundColorPanel.PanelOverride = styleBoxFlat; BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString; OverallPlaytimeLabel.Text = player.PlaytimeString;
PlayerEntity = player.NetEntity;
} }
} }

View File

@@ -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
}

View File

@@ -6,18 +6,18 @@
<BoxContainer Orientation="Vertical" Margin="8"> <BoxContainer Orientation="Vertical" Margin="8">
<Label Text="{Loc 'ui-options-admin-player-panel'}" <Label Text="{Loc 'ui-options-admin-player-panel'}"
StyleClasses="LabelKeyText"/> StyleClasses="LabelKeyText"/>
<CheckBox Name="PlayerlistSeparateSymbolsCheckBox" Text="{Loc 'ui-options-admin-playerlist-separate-symbols'}" /> <ui:OptionDropDown Name="DropDownPlayerTabSymbolSetting" Title="{Loc 'ui-options-admin-player-tab-symbol-setting'}" />
<CheckBox Name="PlayerlistCharacterColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-character-color'}" /> <ui:OptionDropDown Name="DropDownPlayerTabRoleSetting" Title="{Loc 'ui-options-admin-player-tab-role-setting'}" />
<CheckBox Name="PlayerlistRoleTypeColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-roletype-color'}" /> <ui:OptionDropDown Name="DropDownPlayerTabColorSetting" Title="{Loc 'ui-options-admin-player-tab-color-setting'}" />
<Label Text="{Loc 'ui-options-admin-overlay-title'}" <Label Text="{Loc 'ui-options-admin-overlay-title'}"
StyleClasses="LabelKeyText"/> StyleClasses="LabelKeyText"/>
<CheckBox Name="EnableClassicOverlayCheckBox" Text="{Loc 'ui-options-enable-classic-overlay'}" /> <ui:OptionDropDown Name="DropDownOverlayAntagFormat" Title="{Loc 'ui-options-admin-overlay-antag-format'}" />
<CheckBox Name="EnableOverlaySymbolsCheckBox" Text="{Loc 'ui-options-enable-overlay-symbols'}" /> <ui:OptionDropDown Name="DropDownOverlayAntagSymbol" Title="{Loc 'ui-options-admin-overlay-antag-symbol'}" />
<CheckBox Name="EnableOverlayPlaytimeCheckBox" Text="{Loc 'ui-options-enable-overlay-playtime'}" /> <CheckBox Name="EnableOverlayPlaytimeCheckBox" Text="{Loc 'ui-options-admin-enable-overlay-playtime'}" />
<CheckBox Name="EnableOverlayStartingJobCheckBox" Text="{Loc 'ui-options-enable-overlay-starting-job'}" /> <CheckBox Name="EnableOverlayStartingJobCheckBox" Text="{Loc 'ui-options-admin-enable-overlay-starting-job'}" />
<ui:OptionSlider Name="OverlayMergeDistanceSlider" Title="{Loc 'ui-options-overlay-merge-distance'}"/> <ui:OptionSlider Name="OverlayMergeDistanceSlider" Title="{Loc 'ui-options-admin-overlay-merge-distance'}"/>
<ui:OptionSlider Name="OverlayGhostFadeSlider" Title="{Loc 'ui-options-overlay-ghost-fade-distance'}"/> <ui:OptionSlider Name="OverlayGhostFadeSlider" Title="{Loc 'ui-options-admin-overlay-ghost-fade-distance'}"/>
<ui:OptionSlider Name="OverlayGhostHideSlider" Title="{Loc 'ui-options-overlay-ghost-hide-distance'}"/> <ui:OptionSlider Name="OverlayGhostHideSlider" Title="{Loc 'ui-options-admin-overlay-ghost-hide-distance'}"/>
</BoxContainer> </BoxContainer>
</ScrollContainer> </ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" /> <ui:OptionsTabControlRow Name="Control" Access="Public" />

View File

@@ -1,3 +1,5 @@
using Content.Client.Administration;
using Content.Client.Administration.UI.Tabs.PlayerTab;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -19,12 +21,43 @@ public sealed partial class AdminOptionsTab : Control
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistSeparateSymbols, PlayerlistSeparateSymbolsCheckBox); var antagFormats = new List<OptionDropDownCVar<string>.ValueOption>();
Control.AddOptionCheckBox(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerlistCharacterColorCheckBox); foreach (var format in Enum.GetValues(typeof(AdminOverlayAntagFormat)))
Control.AddOptionCheckBox(CCVars.AdminPlayerlistRoleTypeColor, PlayerlistRoleTypeColorCheckBox); {
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.AdminOverlayPlaytime, EnableOverlayPlaytimeCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayStartingJob, EnableOverlayStartingJobCheckBox); Control.AddOptionCheckBox(CCVars.AdminOverlayStartingJob, EnableOverlayStartingJobCheckBox);

View File

@@ -221,18 +221,11 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
if (!_ent.TryGetComponent<MindComponent>(container.Mind.Value, out var mind)) if (!_ent.TryGetComponent<MindComponent>(container.Mind.Value, out var mind))
return; return;
var roleText = Loc.GetString("role-type-crew-aligned-name"); if (!_prototypeManager.TryIndex(mind.RoleType, out var proto))
var color = Color.White; _sawmill.Error($"Player '{_player.LocalSession}' has invalid Role Type '{mind.RoleType}'. Displaying default instead");
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");
_window.RoleType.Text = roleText; _window.RoleType.Text = Loc.GetString(proto?.Name ?? "role-type-crew-aligned-name");
_window.RoleType.FontColorOverride = color; _window.RoleType.FontColorOverride = proto?.Color ?? Color.White;
} }
private void CharacterDetached(EntityUid uid) private void CharacterDetached(EntityUid uid)

View File

@@ -235,12 +235,16 @@ 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;
LocId? subtype = null;
if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null) if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
{ {
sortWeight = _role.GetRoleCompByTime(mindComp)?.Comp.SortWeight ?? 0; 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;
subtype = mindComp.Subtype;
}
else else
Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead"); 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, startingRole,
antag, antag,
roleType, roleType,
subtype,
sortWeight, sortWeight,
GetNetEntity(session?.AttachedEntity), GetNetEntity(session?.AttachedEntity),
data.UserId, data.UserId,

View File

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

View File

@@ -38,10 +38,13 @@ public sealed partial class CCVars
CVarDef.Create("outline.enabled", true, CVar.CLIENTONLY); CVarDef.Create("outline.enabled", true, CVar.CLIENTONLY);
/// <summary> /// <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> /// </summary>
public static readonly CVarDef<bool> AdminOverlayClassic = public static readonly CVarDef<string> AdminOverlayAntagFormat =
CVarDef.Create("ui.admin_overlay_classic", false, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_overlay_antag_format", "Subtype", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <summary>
/// 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
@@ -50,34 +53,48 @@ public sealed partial class CCVars
CVarDef.Create("ui.admin_overlay_playtime", true, 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 player's starting role.
/// </summary> /// </summary>
public static readonly CVarDef<bool> AdminOverlayStartingJob = public static readonly CVarDef<bool> AdminOverlayStartingJob =
CVarDef.Create("ui.admin_overlay_starting_job", true, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_overlay_starting_job", true, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <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> /// </summary>
public static readonly CVarDef<bool> AdminPlayerlistSeparateSymbols = public static readonly CVarDef<string> AdminPlayerTabSymbolSetting =
CVarDef.Create("ui.admin_playerlist_separate_symbols", false, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_player_tab_symbols", "Specific", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <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> /// </summary>
public static readonly CVarDef<bool> AdminPlayerlistHighlightedCharacterColor = public static readonly CVarDef<string> AdminPlayerTabColorSetting =
CVarDef.Create("ui.admin_playerlist_highlighted_character_color", true, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_player_tab_color", "Both", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <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> /// </summary>
public static readonly CVarDef<bool> AdminPlayerlistRoleTypeColor = public static readonly CVarDef<string> AdminPlayerTabRoleSetting =
CVarDef.Create("ui.admin_playerlist_role_type_color", true, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_player_tab_role", "Subtype", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <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> /// </summary>
public static readonly CVarDef<bool> AdminOverlaySymbols = public static readonly CVarDef<string> AdminOverlaySymbolStyle =
CVarDef.Create("ui.admin_overlay_symbols", true, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_overlay_symbol_style", "Specific", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary> /// <summary>
/// The range (in tiles) around the cursor within which the admin overlays of ghosts start to fade out /// The range (in tiles) around the cursor within which the admin overlays of ghosts start to fade out

View File

@@ -108,6 +108,12 @@ public sealed partial class MindComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public ProtoId<RoleTypePrototype> RoleType = "Neutral"; 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> /// <summary>
/// The session of the player owning this mind. /// The session of the player owning this mind.
/// Can be null, in which case the player is currently not logged in. /// Can be null, in which case the player is currently not logged in.

View File

@@ -21,7 +21,7 @@ public sealed partial class RoleTypePrototype : IPrototype
/// The role's displayed color. /// The role's displayed color.
/// </summary> /// </summary>
[DataField] [DataField]
public Color Color { get; private set; } = Color.FromHex("#eeeeee"); public Color Color = Color.FromHex("#eeeeee");
/// <summary> /// <summary>
/// A symbol used to represent the role type. /// A symbol used to represent the role type.

View 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. /// A single antag Mind Role is enough to make the owner mind count as Antagonist.
/// </summary> /// </summary>
[DataField] [DataField]
public bool Antag { get; set; } = false; public bool Antag;
/// <summary> /// <summary>
/// The mind's current antagonist/special role, or lack thereof; /// The mind's current antagonist/special role, or lack thereof;
@@ -23,11 +23,17 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
[DataField] [DataField]
public ProtoId<RoleTypePrototype>? RoleType; public ProtoId<RoleTypePrototype>? RoleType;
/// <summary>
/// The role's subtype, shown only to admins to help with antag categorization
/// </summary>
[DataField]
public LocId? Subtype;
/// <summary> /// <summary>
/// True if this mindrole is an exclusive antagonist. Antag setting is not checked if this is True. /// True if this mindrole is an exclusive antagonist. Antag setting is not checked if this is True.
/// </summary> /// </summary>
[DataField] [DataField]
public bool ExclusiveAntag { get; set; } = false; public bool ExclusiveAntag;
/// <summary> /// <summary>
/// The Mind that this role belongs to /// The Mind that this role belongs to

View File

@@ -6,7 +6,6 @@ using Content.Shared.Database;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles.Jobs; using Content.Shared.Roles.Jobs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
@@ -202,22 +201,22 @@ public abstract class SharedRoleSystem : EntitySystem
return false; return false;
//get the most important/latest mind role //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; return false;
SetRoleType(ent.Owner, roleType); SetRoleType(ent.Owner, roleType, subtype);
return true; return true;
} }
/// <summary> /// <summary>
/// Return the most recently specified role type, or Neutral /// Return the most recently specified role type and subtype, or Neutral
/// </summary> /// </summary>
private ProtoId<RoleTypePrototype> GetRoleTypeByTime(MindComponent mind) private (ProtoId<RoleTypePrototype>, LocId?) GetRoleTypeByTime(MindComponent mind)
{ {
var role = GetRoleCompByTime(mind); var role = GetRoleCompByTime(mind);
return role?.Comp?.RoleType ?? "Neutral"; return (role?.Comp?.RoleType ?? "Neutral", role?.Comp?.Subtype);
} }
/// <summary> /// <summary>
@@ -238,21 +237,22 @@ public abstract class SharedRoleSystem : EntitySystem
return (result); 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)) 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; return;
} }
if (!_prototypes.HasIndex(roleTypeId)) 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; return;
} }
comp.RoleType = roleTypeId; comp.RoleType = roleTypeId;
comp.Subtype = subtype;
Dirty(mind, comp); Dirty(mind, comp);
// Update player character window // Update player character window
@@ -269,13 +269,13 @@ public abstract class SharedRoleSystem : EntitySystem
Log.Error($"{ToPrettyString(mind)} does not have an OwnedEntity!"); Log.Error($"{ToPrettyString(mind)} does not have an OwnedEntity!");
_adminLogger.Add(LogType.Mind, _adminLogger.Add(LogType.Mind,
LogImpact.Medium, LogImpact.Medium,
$"Role Type of {ToPrettyString(mind)} changed to {roleTypeId}"); $"Role Type of {ToPrettyString(mind)} changed to {roleTypeId}, {subtype}");
return; return;
} }
_adminLogger.Add(LogType.Mind, _adminLogger.Add(LogType.Mind,
LogImpact.High, LogImpact.High,
$"Role Type of {ToPrettyString(comp.OwnedEntity)} changed to {roleTypeId}"); $"Role Type of {ToPrettyString(comp.OwnedEntity)} changed to {roleTypeId}, {subtype}");
} }
/// <summary> /// <summary>
@@ -630,6 +630,14 @@ public abstract class SharedRoleSystem : EntitySystem
return antag.Requirements; 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> /// <summary>

View File

@@ -339,15 +339,37 @@ ui-options-censor-nudity = Censor character nudity
ui-options-admin-player-panel = Admin Menu Players List 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-player-tab-symbol-setting = Character column antag symbols
ui-options-admin-playerlist-character-color = Color names of antagonist characters ui-options-admin-player-tab-symbol-setting-off = No antag symbol
ui-options-admin-playerlist-roletype-color = Color role types 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-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-admin-overlay-antag-format = Antag label style
ui-options-enable-overlay-playtime = Show playtime ui-options-admin-overlay-antag-format-binary = Show antag status
ui-options-enable-overlay-starting-job = Show starting job ui-options-admin-overlay-antag-format-roletype = Show role type
ui-options-overlay-merge-distance = Stack merge distance ui-options-admin-overlay-antag-format-subtype = Show subtype
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-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

View File

@@ -17,3 +17,19 @@ role-type-free-agent-color = #ffff00
role-type-familiar-color = #6495ed role-type-familiar-color = #6495ed
role-type-silicon-color = #6495ed role-type-silicon-color = #6495ed
role-type-silicon-antagonist-color =#c832e6 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

View File

@@ -95,7 +95,6 @@
antagPrototype: GenericTeamAntagonist 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 # 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 - type: entity
parent: MindRoleGhostRoleTeamAntagonist parent: MindRoleGhostRoleTeamAntagonist
id: MindRoleGhostRoleTeamAntagonistFlock id: MindRoleGhostRoleTeamAntagonistFlock
@@ -130,6 +129,7 @@
- type: MindRole - type: MindRole
antagPrototype: SubvertedSilicon antagPrototype: SubvertedSilicon
roleType: SiliconAntagonist roleType: SiliconAntagonist
subtype: role-subtype-subverted
- type: SubvertedSiliconRole - type: SubvertedSiliconRole
# Dragon # Dragon
@@ -141,6 +141,7 @@
- type: MindRole - type: MindRole
antagPrototype: Dragon antagPrototype: Dragon
roleType: TeamAntagonist roleType: TeamAntagonist
subtype: role-subtype-dragon
exclusiveAntag: true exclusiveAntag: true
- type: DragonRole - type: DragonRole
- type: RoleBriefing - type: RoleBriefing
@@ -154,6 +155,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: SpaceNinja antagPrototype: SpaceNinja
subtype: role-subtype-ninja
exclusiveAntag: true exclusiveAntag: true
- type: NinjaRole - type: NinjaRole
@@ -165,7 +167,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: ParadoxClone antagPrototype: ParadoxClone
roleType: SoloAntagonist subtype: role-subtype-paradox-clone
- type: ParadoxCloneRole - type: ParadoxCloneRole
# Nukies # Nukies
@@ -176,6 +178,7 @@
components: components:
- type: MindRole - type: MindRole
roleType: TeamAntagonist roleType: TeamAntagonist
subtype: role-subtype-nukie
exclusiveAntag: true exclusiveAntag: true
antagPrototype: Nukeops antagPrototype: Nukeops
- type: NukeopsRole - type: NukeopsRole
@@ -206,6 +209,7 @@
antagPrototype: HeadRev antagPrototype: HeadRev
exclusiveAntag: true exclusiveAntag: true
roleType: TeamAntagonist roleType: TeamAntagonist
subtype: role-subtype-head-revolutionary
- type: RevolutionaryRole - type: RevolutionaryRole
- type: entity - type: entity
@@ -215,6 +219,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: Rev antagPrototype: Rev
subtype: role-subtype-revolutionary
# Survivors (Wizard) # Survivors (Wizard)
- type: entity - type: entity
@@ -225,6 +230,7 @@
- type: MindRole - type: MindRole
antagPrototype: Survivor antagPrototype: Survivor
roleType: FreeAgent roleType: FreeAgent
subtype: role-subtype-survivor
- type: SurvivorRole - type: SurvivorRole
# Thief # Thief
@@ -235,6 +241,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: Thief antagPrototype: Thief
subtype: role-subtype-thief
- type: ThiefRole - type: ThiefRole
# Traitors # Traitors
@@ -246,6 +253,7 @@
- type: MindRole - type: MindRole
antagPrototype: Traitor antagPrototype: Traitor
exclusiveAntag: true exclusiveAntag: true
subtype: role-subtype-traitor
- type: TraitorRole - type: TraitorRole
- type: entity - type: entity
@@ -263,6 +271,7 @@
components: components:
- type: MindRole - type: MindRole
roleType: TeamAntagonist roleType: TeamAntagonist
subtype: role-subtype-traitor-reinforcement
antagPrototype: GenericTeamAntagonist antagPrototype: GenericTeamAntagonist
# Wizards # Wizards
@@ -274,6 +283,7 @@
- type: MindRole - type: MindRole
antagPrototype: Wizard antagPrototype: Wizard
exclusiveAntag: true exclusiveAntag: true
subtype: role-subtype-wizard
- type: WizardRole - type: WizardRole
# Zombie Squad # Zombie Squad
@@ -286,6 +296,7 @@
antagPrototype: InitialInfected antagPrototype: InitialInfected
exclusiveAntag: true exclusiveAntag: true
roleType: TeamAntagonist roleType: TeamAntagonist
subtype: role-subtype-initial-infected
- type: InitialInfectedRole - type: InitialInfectedRole
- type: entity - type: entity
@@ -296,5 +307,5 @@
- type: MindRole - type: MindRole
antagPrototype: Zombie antagPrototype: Zombie
exclusiveAntag: true exclusiveAntag: true
roleType: TeamAntagonist subtype: role-subtype-zombie
- type: ZombieRole - type: ZombieRole

View File

@@ -1,5 +1,8 @@
# For use by Role Types # The primary role types are referenced by RP Rules, and were specified based on head admin input.
# Do not touch these # 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! # If you change/add a color here, also change it in role-types.ftl!