* mindcomponent namespace * wip MindRole stuff * admin player tab * mindroletype comment * mindRolePrototype redesign * broken param * wip RoleType implementation * basic role type switching for antags * traitor fix * fix AdminPanel update * the renameningTM * cleanup * feature uncreeping * roletypes on mind roles * update MindComponent.RoleType when MindRoles change * ghostrole configuration * ghostrole config improvements * live update of roleType on the character window * logging stuff and notes * remove thing no one asked for * weh * Mind Role Entities wip * headrev count fix * silicon stuff, cleanup * exclusive antag config, cleanup * jobroleadd overwerite * logging stuff * MindHasRole cleanup, admin log stuff * last second cleanup * ocd * move roletypeprototype to its own file, minor note stuff * remove Roletype.Created * log stuff * roletype setup for ghostroles and autotraitor reinforcements * ghostrole type configs * adjustable admin overlay * cleanup * fix this in its own PR * silicon antagonist * borg stuff * mmi roletype handling * spawnable borg roletype handling * weh * ghost role cleanup * weh * RoleEvent update * polish * log stuff * admin overlay config * ghostrolecomponent cleanup * weh * admin overlay code cleanup * minor cleanup * Obsolete MindRoleAddedEvent * comment * minor code cleanup * MindOnDoGreeting fix * Role update message * fix duplicate job greeting for cyborgs * fix emag job message dupe * nicer-looking role type update * crew aligned * syndicate assault borg role fix * fix test fail * fix a merge mistake * fix LoneOp role type * Update Content.Client/Administration/AdminNameOverlay.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Content.Shared/Roles/SharedRoleSystem.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * comment formatting Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * change logging category Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * fix a space Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * use MindAddRoles Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * get MindComponent from TryGetMind Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * move var declaration outside loop * remove TryComp * take RoleEnum behind the barn * don't use ensurecomp unnecessarily * cvar comments * toggleableghostrolecomponent documentation * skrek * use EntProtoId * mindrole config * merge baserolecomponent into basemindrolecomponent * ai and borg silicon role tweaks * formatting Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * I will end you (the color) Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * use LocId type for a locale id * update RoleEvent documentation * update RoleEvent documentation * remove obsolete MindRoleAddedEvent * refine MindRolesUpdate() * use dependency Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * inject dependency Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * roleType.Name no longer required * reformatted draw code logic * GhostRoleMarkerRoleComponent comment * minor SharedRoleSystem cleanup * StartingMindRoleComponent, unhardcode roundstart silicon * Update Content.Shared/Roles/SharedRoleSystem.cs * remove a whitespace --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
230 lines
7.0 KiB
C#
230 lines
7.0 KiB
C#
using System.Linq;
|
|
using Content.Client.Administration.Systems;
|
|
using Content.Client.UserInterface.Controls;
|
|
using Content.Shared.Administration;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.Player;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
|
|
using static Robust.Client.UserInterface.Controls.BaseButton;
|
|
|
|
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class PlayerTab : Control
|
|
{
|
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
|
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
|
|
|
private const string ArrowUp = "↑";
|
|
private const string ArrowDown = "↓";
|
|
private readonly Color _altColor = Color.FromHex("#292B38");
|
|
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
|
private readonly AdminSystem _adminSystem;
|
|
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
|
|
|
|
private Header _headerClicked = Header.Username;
|
|
private bool _ascending = true;
|
|
private bool _showDisconnected;
|
|
|
|
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
|
|
|
public PlayerTab()
|
|
{
|
|
IoCManager.InjectDependencies(this);
|
|
RobustXamlLoader.Load(this);
|
|
|
|
_adminSystem = _entManager.System<AdminSystem>();
|
|
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
|
_adminSystem.OverlayEnabled += OverlayEnabled;
|
|
_adminSystem.OverlayDisabled += OverlayDisabled;
|
|
|
|
OverlayButton.OnPressed += OverlayButtonPressed;
|
|
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
|
|
|
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
|
ListHeader.OnHeaderClicked += HeaderClicked;
|
|
|
|
SearchList.SearchBar = SearchLineEdit;
|
|
SearchList.GenerateItem += GenerateButton;
|
|
SearchList.DataFilterCondition += DataFilterCondition;
|
|
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
|
|
|
RefreshPlayerList(_adminSystem.PlayerList);
|
|
|
|
}
|
|
|
|
#region Antag Overlay
|
|
|
|
private void OverlayEnabled()
|
|
{
|
|
OverlayButton.Pressed = true;
|
|
}
|
|
|
|
private void OverlayDisabled()
|
|
{
|
|
OverlayButton.Pressed = false;
|
|
}
|
|
|
|
private void OverlayButtonPressed(ButtonEventArgs args)
|
|
{
|
|
if (args.Button.Pressed)
|
|
{
|
|
_adminSystem.AdminOverlayOn();
|
|
}
|
|
else
|
|
{
|
|
_adminSystem.AdminOverlayOff();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
|
{
|
|
_showDisconnected = args.Button.Pressed;
|
|
RefreshPlayerList(_players);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
|
|
if (disposing)
|
|
{
|
|
_adminSystem.PlayerListChanged -= RefreshPlayerList;
|
|
_adminSystem.OverlayEnabled -= OverlayEnabled;
|
|
_adminSystem.OverlayDisabled -= OverlayDisabled;
|
|
|
|
OverlayButton.OnPressed -= OverlayButtonPressed;
|
|
|
|
ListHeader.OnHeaderClicked -= HeaderClicked;
|
|
}
|
|
}
|
|
|
|
#region ListContainer
|
|
|
|
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
|
{
|
|
_players = players;
|
|
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
|
|
|
|
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
|
|
|
|
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
|
|
sortedPlayers.Sort(Compare);
|
|
|
|
UpdateHeaderSymbols();
|
|
|
|
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
|
|
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
|
|
.ToList());
|
|
}
|
|
|
|
private void GenerateButton(ListData data, ListContainerButton button)
|
|
{
|
|
if (data is not PlayerListData { Info: var player})
|
|
return;
|
|
|
|
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
|
|
button.AddChild(entry);
|
|
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
|
|
/// If all characters are lowercase, the comparison ignores case.
|
|
/// If there is an uppercase character, the comparison is case sensitive.
|
|
/// </summary>
|
|
/// <param name="filter"></param>
|
|
/// <param name="listData"></param>
|
|
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
|
|
private bool DataFilterCondition(string filter, ListData listData)
|
|
{
|
|
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
|
|
return false;
|
|
|
|
if (!_showDisconnected && !info.Connected)
|
|
return false;
|
|
|
|
if (IsAllLower(filter))
|
|
{
|
|
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (!playerString.Contains(filter))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool IsAllLower(string input)
|
|
{
|
|
foreach (var c in input)
|
|
{
|
|
if (char.IsLetter(c) && !char.IsLower(c))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Header
|
|
|
|
private void UpdateHeaderSymbols()
|
|
{
|
|
ListHeader.ResetHeaderText();
|
|
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
|
|
}
|
|
|
|
private int Compare(PlayerInfo x, PlayerInfo y)
|
|
{
|
|
if (!_ascending)
|
|
{
|
|
(x, y) = (y, x);
|
|
}
|
|
|
|
return _headerClicked switch
|
|
{
|
|
Header.Username => Compare(x.Username, y.Username),
|
|
Header.Character => Compare(x.CharacterName, y.CharacterName),
|
|
Header.Job => Compare(x.StartingJob, y.StartingJob),
|
|
Header.Antagonist => x.Antag.CompareTo(y.Antag),
|
|
Header.RoleType => Compare(x.RoleProto.Name , y.RoleProto.Name),
|
|
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
|
|
_ => 1
|
|
};
|
|
}
|
|
|
|
private int Compare(string x, string y)
|
|
{
|
|
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private void HeaderClicked(Header header)
|
|
{
|
|
if (_headerClicked == header)
|
|
{
|
|
_ascending = !_ascending;
|
|
}
|
|
else
|
|
{
|
|
_headerClicked = header;
|
|
_ascending = true;
|
|
}
|
|
|
|
RefreshPlayerList(_adminSystem.PlayerList);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
|