Красивое УИ телепорта для призрака (#378)
* add: nice ghost teleport ui * fix: fix unused import * wtf * fuck you, search bar * fix-add: finally
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io" Title="{Loc 'ghost-target-window-title'}" MinSize="450 450" SetSize="450 450">
|
||||
<DefaultWindow xmlns="https://spacestation14.io" Title="{Loc 'ghost-target-window-title'}" MinSize="950 550" SetSize="950 550">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.4">
|
||||
<Button Name="GhostnadoButton" Text="{Loc 'ghost-target-window-warp-to-most-followed'}" HorizontalAlignment="Center" Margin="0 4" />
|
||||
<LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True" Margin="0 4" />
|
||||
<ScrollContainer VerticalExpand="True" HorizontalExpand="True" HScrollEnabled="False">
|
||||
<BoxContainer Name="ButtonContainer" Orientation="Vertical" VerticalExpand="True" SeparationOverride="5">
|
||||
<BoxContainer Name="GhostTeleportContainter" Orientation="Vertical">
|
||||
<!-- Target buttons get added here by code -->
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
|
||||
@@ -1,95 +1,346 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared._White.Antag;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GhostTargetWindow : DefaultWindow
|
||||
{
|
||||
private List<(string, NetEntity)> _warps = new();
|
||||
private string _searchText = string.Empty;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private List<GhostWarpPlayer> _playerWarps = new();
|
||||
private List<GhostWarpPlace> _placeWarps = new();
|
||||
private List<GhostWarpGlobalAntagonist> _globalAntoginists = new();
|
||||
|
||||
private List<GhostWarpPlayer> _alivePlayers = new();
|
||||
private List<GhostWarpPlayer> _leftPlayers = new();
|
||||
private List<GhostWarpPlayer> _deadPlayers = new();
|
||||
private List<GhostWarpPlayer> _ghostPlayers = new();
|
||||
|
||||
public event Action<NetEntity>? WarpClicked;
|
||||
public event Action? OnGhostnadoClicked;
|
||||
|
||||
public GhostTargetWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
SearchBar.OnTextChanged += OnSearchTextChanged;
|
||||
|
||||
GhostnadoButton.OnPressed += _ => OnGhostnadoClicked?.Invoke();
|
||||
}
|
||||
|
||||
public void UpdateWarps(IEnumerable<GhostWarp> warps)
|
||||
{
|
||||
// Server COULD send these sorted but how about we just use the client to do it instead
|
||||
_warps = warps
|
||||
.OrderBy(w => w.IsWarpPoint)
|
||||
.ThenBy(w => w.DisplayName, Comparer<string>.Create(
|
||||
(x, y) => string.Compare(x, y, StringComparison.Ordinal)))
|
||||
.Select(w =>
|
||||
{
|
||||
var name = w.IsWarpPoint
|
||||
? Loc.GetString("ghost-target-window-current-button", ("name", w.DisplayName))
|
||||
: w.DisplayName;
|
||||
|
||||
return (name, w.Entity);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void Populate()
|
||||
{
|
||||
ButtonContainer.DisposeAllChildren();
|
||||
GhostTeleportContainter.DisposeAllChildren();
|
||||
_playerWarps = GetSortedPlayers(_playerWarps);
|
||||
_placeWarps = GetSortedPlaces(_placeWarps);
|
||||
_globalAntoginists = GetSortedAntagonists(_globalAntoginists);
|
||||
|
||||
PlayersAllocation();
|
||||
AddButtons();
|
||||
}
|
||||
|
||||
public void UpdateWarps(List<GhostWarpPlayer> players, List<GhostWarpPlace> places, List<GhostWarpGlobalAntagonist> antagonists)
|
||||
{
|
||||
_playerWarps = players;
|
||||
_placeWarps = places;
|
||||
_globalAntoginists = antagonists;
|
||||
}
|
||||
|
||||
private void AddButtons()
|
||||
{
|
||||
foreach (var (name, warpTarget) in _warps)
|
||||
AddAntagButtons(_globalAntoginists, "ghost-teleport-menu-antagonists-label", "ButtonColorAntagonistDepartment");
|
||||
AddPlayerButtons(_alivePlayers, "ghost-teleport-menu-alive-label", string.Empty, true); // Alive
|
||||
AddPlayerButtons(_deadPlayers, "ghost-teleport-menu-dead-label", string.Empty, true); // Dead
|
||||
AddPlayerButtons(_ghostPlayers, "ghost-teleport-menu-ghosts-label", string.Empty, true); // Ghost
|
||||
AddPlayerButtons(_leftPlayers, "ghost-teleport-menu-left-label", string.Empty, true); // Left
|
||||
AddPlaceButtons(_placeWarps, "ghost-teleport-menu-locations-label", "ButtonColorSpecificDepartment");
|
||||
}
|
||||
|
||||
private void AddPlayerButtons(List<GhostWarpPlayer> players, string text, string styleClass,
|
||||
bool enableByDepartmentColorSheet)
|
||||
{
|
||||
if (players.Count == 0)
|
||||
return;
|
||||
|
||||
var bigGrid = new GridContainer();
|
||||
|
||||
var bigLabel = new Label
|
||||
{
|
||||
var currentButtonRef = new Button
|
||||
Text = Loc.GetString(text),
|
||||
StyleClasses = { "LabelBig" }
|
||||
};
|
||||
bigGrid.AddChild(bigLabel);
|
||||
|
||||
var sortedPlayers = SortPlayersByDepartment(players);
|
||||
|
||||
foreach (var departmentList in sortedPlayers)
|
||||
{
|
||||
if (departmentList.Count == 0)
|
||||
continue;
|
||||
|
||||
var departmentGrid = new GridContainer
|
||||
{
|
||||
Text = name,
|
||||
Columns = 5
|
||||
};
|
||||
|
||||
if (enableByDepartmentColorSheet)
|
||||
styleClass = _prototypeManager.Index<DepartmentPrototype>(departmentList[0].DepartmentID).ButtonStyle;
|
||||
|
||||
var labelText = _prototypeManager.Index<DepartmentPrototype>(departmentList[0].DepartmentID).Name;
|
||||
|
||||
var departmentLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString(labelText) + ": " + departmentList.Count,
|
||||
StyleClasses = { "LabelSecondaryColor" }
|
||||
};
|
||||
|
||||
foreach (var player in departmentList)
|
||||
{
|
||||
var playerButton = new Button
|
||||
{
|
||||
Text = player.Name,
|
||||
TextAlign = Label.AlignMode.Right,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
StyleClasses = { styleClass },
|
||||
ToolTip = player.JobName,
|
||||
TooltipDelay = 0.1f,
|
||||
SetWidth = 180,
|
||||
};
|
||||
|
||||
playerButton.OnPressed += _ => WarpClicked?.Invoke(player.Entity);
|
||||
|
||||
departmentGrid.AddChild(playerButton);
|
||||
}
|
||||
|
||||
bigGrid.AddChild(departmentLabel);
|
||||
bigGrid.AddChild(departmentGrid);
|
||||
}
|
||||
|
||||
GhostTeleportContainter.AddChild(bigGrid);
|
||||
}
|
||||
|
||||
private void AddPlaceButtons(List<GhostWarpPlace> places, string text, string styleClass)
|
||||
{
|
||||
if (places.Count == 0)
|
||||
return;
|
||||
|
||||
var bigGrid = new GridContainer();
|
||||
|
||||
var bigLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString(text),
|
||||
StyleClasses = { "LabelBig" }
|
||||
};
|
||||
bigGrid.AddChild(bigLabel);
|
||||
|
||||
var placesGrid = new GridContainer
|
||||
{
|
||||
Columns = 5,
|
||||
};
|
||||
|
||||
var countLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("ghost-teleport-menu-count-label") + ": " + places.Count,
|
||||
StyleClasses = { "LabelSecondaryColor" }
|
||||
};
|
||||
|
||||
foreach (var place in places)
|
||||
{
|
||||
var placeButton = new Button
|
||||
{
|
||||
Text = place.Name,
|
||||
TextAlign = Label.AlignMode.Right,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
MinSize = new Vector2(340, 20),
|
||||
ClipText = true,
|
||||
StyleClasses = { styleClass },
|
||||
ToolTip = place.Description,
|
||||
TooltipDelay = 0.1f,
|
||||
SetWidth = 180,
|
||||
};
|
||||
|
||||
currentButtonRef.OnPressed += _ => WarpClicked?.Invoke(warpTarget);
|
||||
currentButtonRef.Visible = ButtonIsVisible(currentButtonRef);
|
||||
placeButton.OnPressed += _ => WarpClicked?.Invoke(place.Entity);
|
||||
|
||||
ButtonContainer.AddChild(currentButtonRef);
|
||||
placesGrid.AddChild(placeButton);
|
||||
}
|
||||
|
||||
bigGrid.AddChild(countLabel);
|
||||
bigGrid.AddChild(placesGrid);
|
||||
|
||||
GhostTeleportContainter.AddChild(bigGrid);
|
||||
}
|
||||
|
||||
private bool ButtonIsVisible(Button button)
|
||||
private void AddAntagButtons(List<GhostWarpGlobalAntagonist> antags, string text, string styleClass)
|
||||
{
|
||||
return string.IsNullOrEmpty(_searchText) || button.Text == null || button.Text.Contains(_searchText, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
if (antags.Count == 0)
|
||||
return;
|
||||
|
||||
private void UpdateVisibleButtons()
|
||||
{
|
||||
foreach (var child in ButtonContainer.Children)
|
||||
var bigGrid = new GridContainer();
|
||||
|
||||
var bigLabel = new Label
|
||||
{
|
||||
if (child is Button button)
|
||||
button.Visible = ButtonIsVisible(button);
|
||||
Text = Loc.GetString(text),
|
||||
StyleClasses = { "LabelBig" }
|
||||
};
|
||||
bigGrid.AddChild(bigLabel);
|
||||
|
||||
var sortedAntags = SortAntagsByWeight(antags);
|
||||
|
||||
foreach (var antagList in sortedAntags)
|
||||
{
|
||||
if (antagList.Count == 0)
|
||||
continue;
|
||||
|
||||
var departmentGrid = new GridContainer
|
||||
{
|
||||
Columns = 5
|
||||
};
|
||||
|
||||
var labelText = "ghost-teleport-menu-count-label";
|
||||
|
||||
foreach (var antag in antagList)
|
||||
{
|
||||
var playerButton = new Button
|
||||
{
|
||||
Text = antag.Name,
|
||||
TextAlign = Label.AlignMode.Right,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
StyleClasses = { styleClass },
|
||||
ToolTip = Loc.GetString(antag.AntagonistDescription),
|
||||
TooltipDelay = 0.1f,
|
||||
SetWidth = 180,
|
||||
};
|
||||
|
||||
playerButton.OnPressed += _ => WarpClicked?.Invoke(antag.Entity);
|
||||
|
||||
departmentGrid.AddChild(playerButton);
|
||||
|
||||
labelText = antag.AntagonistName;
|
||||
}
|
||||
|
||||
var departmentLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString(labelText) + ": " + antagList.Count,
|
||||
StyleClasses = { "LabelSecondaryColor" }
|
||||
};
|
||||
|
||||
bigGrid.AddChild(departmentLabel);
|
||||
bigGrid.AddChild(departmentGrid);
|
||||
}
|
||||
|
||||
GhostTeleportContainter.AddChild(bigGrid);
|
||||
}
|
||||
|
||||
private void OnSearchTextChanged(LineEdit.LineEditEventArgs args)
|
||||
public List<List<GhostWarpPlayer>> SortPlayersByDepartment(List<GhostWarpPlayer> players)
|
||||
{
|
||||
_searchText = args.Text;
|
||||
var sortedPlayers = new List<List<GhostWarpPlayer>>();
|
||||
|
||||
UpdateVisibleButtons();
|
||||
players = players.OrderBy(p => _prototypeManager.Index<DepartmentPrototype>(p.DepartmentID).Weight).ToList();
|
||||
|
||||
var currentList = new List<GhostWarpPlayer>();
|
||||
var currentWeight = _prototypeManager.Index<DepartmentPrototype>(players[0].DepartmentID).Weight;
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
var weight = _prototypeManager.Index<DepartmentPrototype>(player.DepartmentID).Weight;
|
||||
|
||||
if (weight == currentWeight)
|
||||
{
|
||||
currentList.Add(player);
|
||||
}
|
||||
else
|
||||
{
|
||||
sortedPlayers.Add(currentList);
|
||||
currentList = new List<GhostWarpPlayer> { player };
|
||||
currentWeight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sortedPlayers.Contains(currentList))
|
||||
sortedPlayers.Add(currentList);
|
||||
|
||||
sortedPlayers.Reverse();
|
||||
return sortedPlayers;
|
||||
}
|
||||
|
||||
public List<List<GhostWarpGlobalAntagonist>> SortAntagsByWeight(List<GhostWarpGlobalAntagonist> antagonists)
|
||||
{
|
||||
var sortedAntags = new List<List<GhostWarpGlobalAntagonist>>();
|
||||
|
||||
antagonists = antagonists.OrderBy(a => _prototypeManager.Index<AntagonistPrototype>(a.PrototypeID).Weight).ToList();
|
||||
|
||||
var currentList = new List<GhostWarpGlobalAntagonist>();
|
||||
var currentWeight = _prototypeManager.Index<AntagonistPrototype>(antagonists[0].PrototypeID).Weight;
|
||||
|
||||
foreach (var antagonist in antagonists)
|
||||
{
|
||||
var weight = _prototypeManager.Index<AntagonistPrototype>(antagonist.PrototypeID).Weight;
|
||||
|
||||
if (weight == currentWeight)
|
||||
{
|
||||
currentList.Add(antagonist);
|
||||
}
|
||||
else
|
||||
{
|
||||
sortedAntags.Add(currentList);
|
||||
currentList = new List<GhostWarpGlobalAntagonist> { antagonist };
|
||||
currentWeight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sortedAntags.Contains(currentList))
|
||||
sortedAntags.Add(currentList);
|
||||
|
||||
return sortedAntags;
|
||||
}
|
||||
|
||||
private List<GhostWarpPlace> GetSortedPlaces(List<GhostWarpPlace> places)
|
||||
{
|
||||
return places
|
||||
.OrderBy(w => w.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<GhostWarpPlayer> GetSortedPlayers(List<GhostWarpPlayer> players)
|
||||
{
|
||||
return players
|
||||
.OrderBy(w => w.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<GhostWarpGlobalAntagonist> GetSortedAntagonists(List<GhostWarpGlobalAntagonist> antagonists)
|
||||
{
|
||||
return antagonists
|
||||
.OrderBy(w => w.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void PlayersAllocation()
|
||||
{
|
||||
_alivePlayers.Clear();
|
||||
_deadPlayers.Clear();
|
||||
_leftPlayers.Clear();
|
||||
_ghostPlayers.Clear();
|
||||
|
||||
foreach (var warp in _playerWarps)
|
||||
{
|
||||
if (warp.IsDead)
|
||||
_deadPlayers.Add(warp);
|
||||
else if (warp.IsLeft)
|
||||
_leftPlayers.Add(warp);
|
||||
else if (warp.IsAlive)
|
||||
_alivePlayers.Add(warp);
|
||||
else if (warp.IsGhost)
|
||||
_ghostPlayers.Add(warp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Ghost;
|
||||
using Content.Client.Ghost;
|
||||
using Content.Client.UserInterface.Systems.Gameplay;
|
||||
using Content.Client.UserInterface.Systems.Ghost.Widgets;
|
||||
using Content.Shared._White.MagGloves;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
@@ -59,9 +59,7 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
||||
public void UpdateGui()
|
||||
{
|
||||
if (Gui == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gui.Visible = _system?.IsGhost ?? false;
|
||||
Gui.Update(_system?.AvailableGhostRoleCount, _system?.Player?.CanReturnToBody);
|
||||
@@ -96,7 +94,7 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
||||
if (Gui?.TargetWindow is not { } window)
|
||||
return;
|
||||
|
||||
window.UpdateWarps(msg.Warps);
|
||||
window.UpdateWarps(msg.Players, msg.Places, msg.Antagonists);
|
||||
window.Populate();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user