Merge pull request #199 from crystallpunk-14/ed-02-06-2024-upstream
Ed 02 06 2024 upstream
This commit is contained in:
@@ -27,6 +27,9 @@ namespace Content.Client.Access.UI
|
||||
private string? _lastJobTitle;
|
||||
private string? _lastJobProto;
|
||||
|
||||
// The job that will be picked if the ID doesn't have a job on the station.
|
||||
private static ProtoId<JobPrototype> _defaultJob = "Passenger";
|
||||
|
||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager,
|
||||
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||
{
|
||||
@@ -65,7 +68,6 @@ namespace Content.Client.Access.UI
|
||||
}
|
||||
|
||||
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
||||
|
||||
_accessButtons.Populate(accessLevels, prototypeManager);
|
||||
AccessLevelControlContainer.AddChild(_accessButtons);
|
||||
|
||||
@@ -172,11 +174,15 @@ namespace Content.Client.Access.UI
|
||||
new List<ProtoId<AccessLevelPrototype>>());
|
||||
|
||||
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
|
||||
if (jobIndex >= 0)
|
||||
// If the job index is < 0 that means they don't have a job registered in the station records.
|
||||
// For example, a new ID from a box would have no job index.
|
||||
if (jobIndex < 0)
|
||||
{
|
||||
JobPresetOptionButton.SelectId(jobIndex);
|
||||
jobIndex = _jobPrototypeIds.IndexOf(_defaultJob);
|
||||
}
|
||||
|
||||
JobPresetOptionButton.SelectId(jobIndex);
|
||||
|
||||
_lastFullName = state.TargetIdFullName;
|
||||
_lastJobTitle = state.TargetIdJobTitle;
|
||||
_lastJobProto = state.TargetIdJobPrototype;
|
||||
|
||||
@@ -147,7 +147,7 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var proto in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
{
|
||||
CreateRoleGroup(proto.ID, proto.Roles, proto.Color);
|
||||
CreateRoleGroup(proto.ID, proto.Roles.Select(p => p.Id), proto.Color);
|
||||
}
|
||||
|
||||
CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes<AntagPrototype>().Select(p => p.ID), Color.Red);
|
||||
|
||||
@@ -16,14 +16,17 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
|
||||
Bwoink.ChannelSelector.OnSelectionChanged += sel =>
|
||||
{
|
||||
if (sel is not null)
|
||||
if (sel is null)
|
||||
{
|
||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||
Title = Loc.GetString("bwoink-none-selected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sel.OverallPlaytime != null)
|
||||
{
|
||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||
}
|
||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||
|
||||
if (sel.OverallPlaytime != null)
|
||||
{
|
||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Content.Client.Administration.UI.CustomControls
|
||||
private List<PlayerInfo> _playerList = new();
|
||||
private readonly List<PlayerInfo> _sortedPlayerList = new();
|
||||
|
||||
public event Action<PlayerInfo>? OnSelectionChanged;
|
||||
public event Action<PlayerInfo?>? OnSelectionChanged;
|
||||
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
||||
|
||||
public Func<PlayerInfo, string, string>? OverrideText;
|
||||
@@ -41,12 +41,19 @@ namespace Content.Client.Administration.UI.CustomControls
|
||||
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||
PlayerListContainer.GenerateItem += GenerateButton;
|
||||
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
|
||||
PopulateList(_adminSystem.PlayerList);
|
||||
FilterLineEdit.OnTextChanged += _ => FilterList();
|
||||
_adminSystem.PlayerListChanged += PopulateList;
|
||||
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
|
||||
}
|
||||
|
||||
private void PlayerListNoItemSelected()
|
||||
{
|
||||
_selectedPlayer = null;
|
||||
OnSelectionChanged?.Invoke(null);
|
||||
}
|
||||
|
||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
|
||||
|
||||
@@ -25,7 +25,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
|
||||
|
||||
public Matrix3 SpaceMatrix;
|
||||
public Matrix3x2 SpaceMatrix;
|
||||
public MapId Map;
|
||||
|
||||
private readonly Font _font;
|
||||
@@ -78,7 +78,8 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (SpaceTiles == null)
|
||||
return;
|
||||
|
||||
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds);
|
||||
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
|
||||
gridBounds = invSpace.TransformBox(args.WorldBounds);
|
||||
|
||||
DrawText(handle, gridBounds, SpaceMatrix, SpaceTiles, SpaceTileSize);
|
||||
}
|
||||
@@ -86,7 +87,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
private void DrawText(
|
||||
DrawingHandleScreen handle,
|
||||
Box2 gridBounds,
|
||||
Matrix3 transform,
|
||||
Matrix3x2 transform,
|
||||
Dictionary<int, List<Vector2i>> tileSets,
|
||||
ushort tileSize)
|
||||
{
|
||||
@@ -103,7 +104,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (!gridBounds.Contains(centre))
|
||||
continue;
|
||||
|
||||
var worldCenter = transform.Transform(centre);
|
||||
var worldCenter = Vector2.Transform(centre, transform);
|
||||
|
||||
var screenCenter = _eyeManager.WorldToScreen(worldCenter);
|
||||
|
||||
@@ -119,7 +120,7 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (tileSets.TryGetValue(0, out var set))
|
||||
{
|
||||
var epicenter = set.First();
|
||||
var worldCenter = transform.Transform((epicenter + Vector2Helpers.Half) * tileSize);
|
||||
var worldCenter = Vector2.Transform((epicenter + Vector2Helpers.Half) * tileSize, transform);
|
||||
var screenCenter = _eyeManager.WorldToScreen(worldCenter) + new Vector2(-24, -24);
|
||||
var text = $"{Intensity[0]:F2}\nΣ={TotalIntensity:F1}\nΔ={Slope:F1}";
|
||||
handle.DrawString(_font, screenCenter, text);
|
||||
@@ -148,11 +149,12 @@ public sealed class ExplosionDebugOverlay : Overlay
|
||||
if (SpaceTiles == null)
|
||||
return;
|
||||
|
||||
gridBounds = Matrix3.Invert(SpaceMatrix).TransformBox(args.WorldBounds).Enlarged(2);
|
||||
Matrix3x2.Invert(SpaceMatrix, out var invSpace);
|
||||
gridBounds = invSpace.TransformBox(args.WorldBounds).Enlarged(2);
|
||||
handle.SetTransform(SpaceMatrix);
|
||||
|
||||
DrawTiles(handle, gridBounds, SpaceTiles, SpaceTileSize);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
private void DrawTiles(
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:pt="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="PlayerCount" HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||
Text="{Loc Player Count}" />
|
||||
<Button Name="ShowDisconnectedButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
||||
Text="{Loc player-tab-show-disconnected}" ToggleMode="True"/>
|
||||
<Button Name="OverlayButton" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"
|
||||
Text="{Loc player-tab-overlay}" ToggleMode="True"/>
|
||||
<Label Name="PlayerCount" HorizontalExpand="True" Text="{Loc Player Count}" />
|
||||
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
||||
PlaceHolder="{Loc player-tab-filter-line-edit-placeholder}" />
|
||||
<Button Name="ShowDisconnectedButton" HorizontalExpand="True"
|
||||
Text="{Loc player-tab-show-disconnected}" ToggleMode="True" />
|
||||
<Button Name="OverlayButton" HorizontalExpand="True" Text="{Loc player-tab-overlay}" ToggleMode="True" />
|
||||
</BoxContainer>
|
||||
<Control MinSize="0 5" />
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" Name="PlayerList">
|
||||
<pt:PlayerTabHeader Name="ListHeader" />
|
||||
<cc:HSeparator />
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<Control MinSize="0 5"/>
|
||||
<pt:PlayerTabHeader Name="ListHeader"/>
|
||||
<cc:HSeparator/>
|
||||
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -28,15 +29,14 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
private bool _ascending = true;
|
||||
private bool _showDisconnected;
|
||||
|
||||
public event Action<PlayerTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
|
||||
public PlayerTab()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
RobustXamlLoader.Load(this);
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
||||
_adminSystem.OverlayEnabled += OverlayEnabled;
|
||||
_adminSystem.OverlayDisabled += OverlayDisabled;
|
||||
@@ -46,8 +46,17 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
|
||||
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;
|
||||
@@ -70,6 +79,8 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
||||
{
|
||||
_showDisconnected = args.Button.Pressed;
|
||||
@@ -92,14 +103,10 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
}
|
||||
}
|
||||
|
||||
#region ListContainer
|
||||
|
||||
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
||||
{
|
||||
foreach (var child in PlayerList.Children.ToArray())
|
||||
{
|
||||
if (child is PlayerTabEntry)
|
||||
child.Dispose();
|
||||
}
|
||||
|
||||
_players = players;
|
||||
PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
|
||||
|
||||
@@ -108,29 +115,66 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
|
||||
UpdateHeaderSymbols();
|
||||
|
||||
var useAltColor = false;
|
||||
foreach (var player in sortedPlayers)
|
||||
{
|
||||
if (!_showDisconnected && !player.Connected)
|
||||
continue;
|
||||
|
||||
var entry = new PlayerTabEntry(player.Username,
|
||||
player.CharacterName,
|
||||
player.IdentityName,
|
||||
player.StartingJob,
|
||||
player.Antag ? "YES" : "NO",
|
||||
new StyleBoxFlat(useAltColor ? _altColor : _defaultColor),
|
||||
player.Connected,
|
||||
player.PlaytimeString);
|
||||
entry.PlayerEntity = player.NetEntity;
|
||||
entry.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(entry, args);
|
||||
entry.ToolTip = Loc.GetString("player-tab-entry-tooltip");
|
||||
PlayerList.AddChild(entry);
|
||||
|
||||
useAltColor ^= true;
|
||||
}
|
||||
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();
|
||||
@@ -174,5 +218,9 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ContainerButton xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer Name="BackgroundColorPanel"/>
|
||||
<PanelContainer xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Name="BackgroundColorPanel">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
@@ -15,17 +15,18 @@
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="JobLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
SizeFlagsStretchRatio="2"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="AntagonistLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="OverallPlaytimeLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</ContainerButton>
|
||||
</PanelContainer>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -6,23 +7,24 @@ using Robust.Client.UserInterface.XAML;
|
||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerTabEntry : ContainerButton
|
||||
public sealed partial class PlayerTabEntry : PanelContainer
|
||||
{
|
||||
public NetEntity? PlayerEntity;
|
||||
|
||||
public PlayerTabEntry(string username, string character, string identity, string job, string antagonist, StyleBox styleBox, bool connected, string overallPlaytime)
|
||||
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
UsernameLabel.Text = username;
|
||||
if (!connected)
|
||||
UsernameLabel.Text = player.Username;
|
||||
if (!player.Connected)
|
||||
UsernameLabel.StyleClasses.Add("Disabled");
|
||||
JobLabel.Text = job;
|
||||
CharacterLabel.Text = character;
|
||||
if (identity != character)
|
||||
CharacterLabel.Text += $" [{identity}]";
|
||||
AntagonistLabel.Text = antagonist;
|
||||
BackgroundColorPanel.PanelOverride = styleBox;
|
||||
OverallPlaytimeLabel.Text = overallPlaytime;
|
||||
JobLabel.Text = player.StartingJob;
|
||||
CharacterLabel.Text = player.CharacterName;
|
||||
if (player.IdentityName != player.CharacterName)
|
||||
CharacterLabel.Text += $" [{player.IdentityName}]";
|
||||
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
|
||||
BackgroundColorPanel.PanelOverride = styleBoxFlat;
|
||||
OverallPlaytimeLabel.Text = player.PlaytimeString;
|
||||
PlayerEntity = player.NetEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,25 @@
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="JobLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
SizeFlagsStretchRatio="2"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc player-tab-job}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="AntagonistLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc player-tab-antagonist}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="PlaytimeLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc player-tab-playtime}"
|
||||
MouseFilter="Pass"/>
|
||||
MouseFilter="Pass"
|
||||
ToolTip="{Loc player-tab-entry-tooltip}"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -66,7 +66,7 @@ public sealed class AtmosDebugOverlay : Overlay
|
||||
DrawData(msg, handle);
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
private void DrawData(DebugMessage msg,
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
|
||||
var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv();
|
||||
state.drawHandle.SetTransform(worldMatrix);
|
||||
var floatBounds = invMatrix.TransformBox(in state.WorldBounds).Enlarged(grid.TileSize);
|
||||
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
|
||||
var localBounds = new Box2i(
|
||||
(int) MathF.Floor(floatBounds.Left),
|
||||
(int) MathF.Floor(floatBounds.Bottom),
|
||||
@@ -249,7 +249,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
});
|
||||
|
||||
drawHandle.UseShader(null);
|
||||
drawHandle.SetTransform(Matrix3.Identity);
|
||||
drawHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
private void DrawMapOverlay(
|
||||
|
||||
@@ -24,8 +24,11 @@ public sealed class ChasmFallingVisualsSystem : EntitySystem
|
||||
|
||||
private void OnComponentInit(EntityUid uid, ChasmFallingComponent component, ComponentInit args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite) ||
|
||||
TerminatingOrDeleted(uid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.OriginalScale = sprite.Scale;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -19,6 +20,7 @@ public sealed partial class EmotesMenu : RadialMenu
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
|
||||
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
|
||||
|
||||
@@ -28,6 +30,7 @@ public sealed partial class EmotesMenu : RadialMenu
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
|
||||
var main = FindControl<RadialContainer>("Main");
|
||||
|
||||
@@ -37,8 +40,8 @@ public sealed partial class EmotesMenu : RadialMenu
|
||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
||||
if (emote.Category == EmoteCategory.Invalid ||
|
||||
emote.ChatTriggers.Count == 0 ||
|
||||
!(player.HasValue && (emote.Whitelist?.IsValid(player.Value, _entManager) ?? true)) ||
|
||||
(emote.Blacklist?.IsValid(player.Value, _entManager) ?? false))
|
||||
!(player.HasValue && _whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) ||
|
||||
_whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
|
||||
continue;
|
||||
|
||||
if (!emote.Available &&
|
||||
|
||||
@@ -38,9 +38,9 @@ namespace Content.Client.Clickable
|
||||
renderOrder = sprite.RenderOrder;
|
||||
var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery);
|
||||
var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation);
|
||||
bottom = Matrix3.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
|
||||
bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
|
||||
|
||||
var invSpriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix());
|
||||
Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix);
|
||||
|
||||
// This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
|
||||
var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
|
||||
@@ -48,8 +48,8 @@ namespace Content.Client.Clickable
|
||||
Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
|
||||
|
||||
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
|
||||
var entityXform = Matrix3.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
|
||||
var localPos = invSpriteMatrix.Transform(entityXform.Transform(worldPos));
|
||||
var entityXform = Matrix3Helpers.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
|
||||
var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix);
|
||||
|
||||
// Check explicitly defined click-able bounds
|
||||
if (CheckDirBound(sprite, relativeRotation, localPos))
|
||||
@@ -79,8 +79,8 @@ namespace Content.Client.Clickable
|
||||
|
||||
// convert to layer-local coordinates
|
||||
layer.GetLayerDrawMatrix(dir, out var matrix);
|
||||
var inverseMatrix = Matrix3.Invert(matrix);
|
||||
var layerLocal = inverseMatrix.Transform(localPos);
|
||||
Matrix3x2.Invert(matrix, out var inverseMatrix);
|
||||
var layerLocal = Vector2.Transform(localPos, inverseMatrix);
|
||||
|
||||
// Convert to image coordinates
|
||||
var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f);
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Linq;
|
||||
using Content.Client.UserInterface.Systems.MenuBar.Widgets;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Placement;
|
||||
@@ -23,6 +24,7 @@ namespace Content.Client.Construction.UI
|
||||
/// </summary>
|
||||
internal sealed class ConstructionMenuPresenter : IDisposable
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
||||
@@ -30,6 +32,7 @@ namespace Content.Client.Construction.UI
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
private readonly IConstructionMenuView _constructionView;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
|
||||
private ConstructionSystem? _constructionSystem;
|
||||
private ConstructionPrototype? _selected;
|
||||
@@ -78,6 +81,7 @@ namespace Content.Client.Construction.UI
|
||||
// This is a lot easier than a factory
|
||||
IoCManager.InjectDependencies(this);
|
||||
_constructionView = new ConstructionMenu();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
|
||||
// This is required so that if we load after the system is initialized, we can bind to it immediately
|
||||
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
|
||||
@@ -160,7 +164,7 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
if (_playerManager.LocalSession == null
|
||||
|| _playerManager.LocalEntity == null
|
||||
|| (recipe.EntityWhitelist != null && !recipe.EntityWhitelist.IsValid(_playerManager.LocalEntity.Value)))
|
||||
|| _whitelistSystem.IsWhitelistFail(recipe.EntityWhitelist, _playerManager.LocalEntity.Value))
|
||||
continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(search))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -113,7 +114,7 @@ namespace Content.Client.Decals.Overlays
|
||||
handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color);
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public sealed class DecalPlacementOverlay : Overlay
|
||||
var handle = args.WorldHandle;
|
||||
handle.SetTransform(worldMatrix);
|
||||
|
||||
var localPos = invMatrix.Transform(mousePos.Position);
|
||||
var localPos = Vector2.Transform(mousePos.Position, invMatrix);
|
||||
|
||||
if (snap)
|
||||
{
|
||||
@@ -63,6 +63,6 @@ public sealed class DecalPlacementOverlay : Overlay
|
||||
var box = new Box2Rotated(aabb, rotation, localPos);
|
||||
|
||||
handle.DrawTextureRect(_sprite.Frame0(decal.Sprite), box, color);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ public sealed class DoAfterOverlay : Overlay
|
||||
|
||||
// If you use the display UI scale then need to set max(1f, displayscale) because 0 is valid.
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
@@ -91,9 +91,9 @@ public sealed class DoAfterOverlay : Overlay
|
||||
? curTime - _meta.GetPauseTime(uid, meta)
|
||||
: curTime;
|
||||
|
||||
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
||||
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
||||
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
||||
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition);
|
||||
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
|
||||
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
|
||||
handle.SetTransform(matty);
|
||||
|
||||
var offset = 0f;
|
||||
@@ -151,7 +151,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
}
|
||||
|
||||
handle.UseShader(null);
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
public Color GetProgressColor(float progress, float alpha = 1f)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Drugs;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -10,6 +12,7 @@ namespace Content.Client.Drugs;
|
||||
|
||||
public sealed class RainbowOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -75,6 +78,10 @@ public sealed class RainbowOverlay : Overlay
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
// TODO disable only the motion part or ike's idea (single static frame of the overlay)
|
||||
if (_config.GetCVar(CCVars.ReducedMotion))
|
||||
return;
|
||||
|
||||
if (ScreenTexture == null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -153,7 +153,6 @@ namespace Content.Client.Entry
|
||||
_parallaxManager.LoadDefaultParallax();
|
||||
|
||||
_overlayManager.AddOverlay(new SingularityOverlay());
|
||||
_overlayManager.AddOverlay(new FlashOverlay());
|
||||
_overlayManager.AddOverlay(new RadiationPulseOverlay());
|
||||
_chatManager.Initialize();
|
||||
_clientPreferencesManager.Initialize();
|
||||
|
||||
@@ -239,9 +239,7 @@ namespace Content.Client.Examine
|
||||
|
||||
if (knowTarget)
|
||||
{
|
||||
// TODO: FormattedMessage.RemoveMarkupPermissive
|
||||
// var itemName = FormattedMessage.RemoveMarkupPermissive(Identity.Name(target, EntityManager, player));
|
||||
var itemName = FormattedMessage.FromMarkupPermissive(Identity.Name(target, EntityManager, player)).ToString();
|
||||
var itemName = FormattedMessage.EscapeText(Identity.Name(target, EntityManager, player));
|
||||
var labelMessage = FormattedMessage.FromMarkupPermissive($"[bold]{itemName}[/bold]");
|
||||
var label = new RichTextLabel();
|
||||
label.SetMessage(labelMessage);
|
||||
@@ -250,7 +248,7 @@ namespace Content.Client.Examine
|
||||
else
|
||||
{
|
||||
var label = new RichTextLabel();
|
||||
label.SetMessage(FormattedMessage.FromMarkup("[bold]???[/bold]"));
|
||||
label.SetMessage(FormattedMessage.FromMarkupOrThrow("[bold]???[/bold]"));
|
||||
hBox.AddChild(label);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class ExplosionOverlay : Overlay
|
||||
DrawExplosion(drawHandle, args.WorldBounds, visuals, index, xforms, textures);
|
||||
}
|
||||
|
||||
drawHandle.SetTransform(Matrix3.Identity);
|
||||
drawHandle.SetTransform(Matrix3x2.Identity);
|
||||
drawHandle.UseShader(null);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@ public sealed class ExplosionOverlay : Overlay
|
||||
if (visuals.SpaceTiles == null)
|
||||
return;
|
||||
|
||||
gridBounds = Matrix3.Invert(visuals.SpaceMatrix).TransformBox(worldBounds).Enlarged(2);
|
||||
Matrix3x2.Invert(visuals.SpaceMatrix, out var invSpace);
|
||||
gridBounds = invSpace.TransformBox(worldBounds).Enlarged(2);
|
||||
drawHandle.SetTransform(visuals.SpaceMatrix);
|
||||
|
||||
DrawTiles(drawHandle, gridBounds, index, visuals.SpaceTiles, visuals, visuals.SpaceTileSize, textures);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.Flash.Components;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Client.Viewport;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
@@ -17,66 +16,87 @@ namespace Content.Client.Flash
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly StatusEffectsSystem _statusSys;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
private readonly ShaderInstance _shader;
|
||||
private double _startTime = -1;
|
||||
private double _lastsFor = 1;
|
||||
private Texture? _screenshotTexture;
|
||||
public float PercentComplete = 0.0f;
|
||||
public Texture? ScreenshotTexture;
|
||||
|
||||
public FlashOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").Instance().Duplicate();
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("FlashedEffect").InstanceUnique();
|
||||
_statusSys = _entityManager.System<StatusEffectsSystem>();
|
||||
}
|
||||
|
||||
public void ReceiveFlash(double duration)
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
var playerEntity = _playerManager.LocalEntity;
|
||||
|
||||
if (playerEntity == null)
|
||||
return;
|
||||
|
||||
if (!_entityManager.HasComponent<FlashedComponent>(playerEntity)
|
||||
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
|
||||
return;
|
||||
|
||||
if (!_statusSys.TryGetTime(playerEntity.Value, SharedFlashSystem.FlashedKey, out var time, status))
|
||||
return;
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
var lastsFor = (float) (time.Value.Item2 - time.Value.Item1).TotalSeconds;
|
||||
var timeDone = (float) (curTime - time.Value.Item1).TotalSeconds;
|
||||
|
||||
PercentComplete = timeDone / lastsFor;
|
||||
}
|
||||
|
||||
public void ReceiveFlash()
|
||||
{
|
||||
if (_stateManager.CurrentState is IMainViewportState state)
|
||||
{
|
||||
// take a screenshot
|
||||
// note that the callback takes a while and ScreenshotTexture will be null the first few Draws
|
||||
state.Viewport.Viewport.Screenshot(image =>
|
||||
{
|
||||
var rgba32Image = image.CloneAs<Rgba32>(SixLabors.ImageSharp.Configuration.Default);
|
||||
_screenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
|
||||
ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_startTime = _gameTiming.CurTime.TotalSeconds;
|
||||
_lastsFor = duration;
|
||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
|
||||
return false;
|
||||
if (args.Viewport.Eye != eyeComp.Eye)
|
||||
return false;
|
||||
|
||||
return PercentComplete < 1.0f;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
|
||||
return;
|
||||
|
||||
if (args.Viewport.Eye != eyeComp.Eye)
|
||||
return;
|
||||
|
||||
var percentComplete = (float) ((_gameTiming.CurTime.TotalSeconds - _startTime) / _lastsFor);
|
||||
if (percentComplete >= 1.0f)
|
||||
if (ScreenshotTexture == null)
|
||||
return;
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
_shader.SetParameter("percentComplete", PercentComplete);
|
||||
worldHandle.UseShader(_shader);
|
||||
_shader.SetParameter("percentComplete", percentComplete);
|
||||
|
||||
if (_screenshotTexture != null)
|
||||
{
|
||||
worldHandle.DrawTextureRectRegion(_screenshotTexture, args.WorldBounds);
|
||||
}
|
||||
|
||||
worldHandle.DrawTextureRectRegion(ScreenshotTexture, args.WorldBounds);
|
||||
worldHandle.UseShader(null);
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
base.DisposeBehavior();
|
||||
_screenshotTexture = null;
|
||||
ScreenshotTexture = null;
|
||||
PercentComplete = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,67 @@
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.Flash.Components;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Flash
|
||||
namespace Content.Client.Flash;
|
||||
|
||||
public sealed class FlashSystem : SharedFlashSystem
|
||||
{
|
||||
public sealed class FlashSystem : SharedFlashSystem
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
|
||||
private FlashOverlay _overlay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
base.Initialize();
|
||||
|
||||
public override void Initialize()
|
||||
SubscribeLocalEvent<FlashedComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<FlashedComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FlashedComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<FlashedComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<FlashedComponent, StatusEffectAddedEvent>(OnStatusAdded);
|
||||
|
||||
_overlay = new();
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(EntityUid uid, FlashedComponent component, LocalPlayerAttachedEvent args)
|
||||
{
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args)
|
||||
{
|
||||
_overlay.PercentComplete = 1.0f;
|
||||
_overlay.ScreenshotTexture = null;
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, FlashedComponent component, ComponentInit args)
|
||||
{
|
||||
if (_player.LocalEntity == uid)
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FlashableComponent, ComponentHandleState>(OnFlashableHandleState);
|
||||
_overlayMan.AddOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFlashableHandleState(EntityUid uid, FlashableComponent component, ref ComponentHandleState args)
|
||||
private void OnShutdown(EntityUid uid, FlashedComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (_player.LocalEntity == uid)
|
||||
{
|
||||
if (args.Current is not FlashableComponentState state)
|
||||
return;
|
||||
_overlay.PercentComplete = 1.0f;
|
||||
_overlay.ScreenshotTexture = null;
|
||||
_overlayMan.RemoveOverlay(_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
// Yes, this code is awful. I'm just porting it to an entity system so don't blame me.
|
||||
if (_playerManager.LocalEntity != uid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.Time == default)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Few things here:
|
||||
// 1. If a shorter duration flash is applied then don't do anything
|
||||
// 2. If the client-side time is later than when the flash should've ended don't do anything
|
||||
var currentTime = _gameTiming.CurTime.TotalSeconds;
|
||||
var newEndTime = state.Time.TotalSeconds + state.Duration;
|
||||
var currentEndTime = component.LastFlash.TotalSeconds + component.Duration;
|
||||
|
||||
if (currentEndTime > newEndTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTime > newEndTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.LastFlash = state.Time;
|
||||
component.Duration = state.Duration;
|
||||
|
||||
var overlay = _overlayManager.GetOverlay<FlashOverlay>();
|
||||
overlay.ReceiveFlash(component.Duration);
|
||||
private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args)
|
||||
{
|
||||
if (_player.LocalEntity == uid && args.Key == FlashedKey)
|
||||
{
|
||||
_overlay.ReceiveFlash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using System.Numerics;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
@@ -73,7 +74,7 @@ public sealed class PuddleOverlay : Overlay
|
||||
}
|
||||
}
|
||||
|
||||
drawHandle.SetTransform(Matrix3.Identity);
|
||||
drawHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
private void DrawScreen(in OverlayDrawArgs args)
|
||||
@@ -99,7 +100,7 @@ public sealed class PuddleOverlay : Overlay
|
||||
if (!gridBounds.Contains(centre))
|
||||
continue;
|
||||
|
||||
var screenCenter = _eyeManager.WorldToScreen(matrix.Transform(centre));
|
||||
var screenCenter = _eyeManager.WorldToScreen(Vector2.Transform(centre, matrix));
|
||||
|
||||
drawHandle.DrawString(_font, screenCenter, debugOverlayData.CurrentVolume.ToString(), Color.White);
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
|
||||
public void FakeClientActivateInWorld(EntityUid activated)
|
||||
{
|
||||
var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated);
|
||||
var activateMsg = new ActivateInWorldEvent(GetGuidebookUser(), activated, true);
|
||||
RaiseLocalEvent(activated, activateMsg);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ public sealed class Box : BoxContainer, IDocumentTag
|
||||
HorizontalExpand = true;
|
||||
control = this;
|
||||
|
||||
if (args.TryGetValue("Margin", out var margin))
|
||||
Margin = new Thickness(float.Parse(margin));
|
||||
|
||||
if (args.TryGetValue("Orientation", out var orientation))
|
||||
Orientation = Enum.Parse<LayoutOrientation>(orientation);
|
||||
else
|
||||
|
||||
49
Content.Client/Guidebook/Richtext/ColorBox.cs
Normal file
49
Content.Client/Guidebook/Richtext/ColorBox.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Guidebook.Richtext;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class ColorBox : PanelContainer, IDocumentTag
|
||||
{
|
||||
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
|
||||
{
|
||||
HorizontalExpand = true;
|
||||
VerticalExpand = true;
|
||||
control = this;
|
||||
|
||||
if (args.TryGetValue("Margin", out var margin))
|
||||
Margin = new Thickness(float.Parse(margin));
|
||||
|
||||
if (args.TryGetValue("HorizontalAlignment", out var halign))
|
||||
HorizontalAlignment = Enum.Parse<HAlignment>(halign);
|
||||
else
|
||||
HorizontalAlignment = HAlignment.Stretch;
|
||||
|
||||
if (args.TryGetValue("VerticalAlignment", out var valign))
|
||||
VerticalAlignment = Enum.Parse<VAlignment>(valign);
|
||||
else
|
||||
VerticalAlignment = VAlignment.Stretch;
|
||||
|
||||
var styleBox = new StyleBoxFlat();
|
||||
if (args.TryGetValue("Color", out var color))
|
||||
styleBox.BackgroundColor = Color.FromHex(color);
|
||||
|
||||
if (args.TryGetValue("OutlineThickness", out var outlineThickness))
|
||||
styleBox.BorderThickness = new Thickness(float.Parse(outlineThickness));
|
||||
else
|
||||
styleBox.BorderThickness = new Thickness(1);
|
||||
|
||||
if (args.TryGetValue("OutlineColor", out var outlineColor))
|
||||
styleBox.BorderColor = Color.FromHex(outlineColor);
|
||||
else
|
||||
styleBox.BorderColor = Color.White;
|
||||
|
||||
PanelOverride = styleBox;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
27
Content.Client/Guidebook/Richtext/Table.cs
Normal file
27
Content.Client/Guidebook/Richtext/Table.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Guidebook.Richtext;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class Table : TableContainer, IDocumentTag
|
||||
{
|
||||
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
|
||||
{
|
||||
HorizontalExpand = true;
|
||||
control = this;
|
||||
|
||||
if (!args.TryGetValue("Columns", out var columns) || !int.TryParse(columns, out var columnsCount))
|
||||
{
|
||||
Logger.Error("Guidebook tag \"Table\" does not specify required property \"Columns.\"");
|
||||
control = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
Columns = columnsCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Client.Inventory.ClientInventorySystem;
|
||||
using static Robust.Client.UserInterface.Control;
|
||||
|
||||
@@ -53,9 +52,13 @@ namespace Content.Client.Inventory
|
||||
_inv = EntMan.System<InventorySystem>();
|
||||
_cuffable = EntMan.System<SharedCuffableSystem>();
|
||||
|
||||
// TODO update name when identity changes
|
||||
var title = Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner, EntMan)));
|
||||
_strippingMenu = new StrippingMenu(title, this);
|
||||
_strippingMenu.OnClose += Close;
|
||||
|
||||
// TODO use global entity
|
||||
// BUIs are opened and closed while applying comp sates, so spawning entities here is probably not the best idea.
|
||||
_virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Lobby;
|
||||
@@ -70,12 +69,9 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
_profileEditor?.RefreshFlavorText();
|
||||
});
|
||||
|
||||
_configurationManager.OnValueChanged(CCVars.GameRoleTimers, args =>
|
||||
{
|
||||
_profileEditor?.RefreshAntags();
|
||||
_profileEditor?.RefreshJobs();
|
||||
_profileEditor?.RefreshLoadouts();
|
||||
});
|
||||
_configurationManager.OnValueChanged(CCVars.GameRoleTimers, _ => RefreshProfileEditor());
|
||||
|
||||
_configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor());
|
||||
}
|
||||
|
||||
private LobbyCharacterPreviewPanel? GetLobbyPreview()
|
||||
@@ -193,6 +189,13 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
PreviewPanel.SetSummaryText(humanoid.Summary);
|
||||
}
|
||||
|
||||
private void RefreshProfileEditor()
|
||||
{
|
||||
_profileEditor?.RefreshAntags();
|
||||
_profileEditor?.RefreshJobs();
|
||||
_profileEditor?.RefreshLoadouts();
|
||||
}
|
||||
|
||||
private void SaveProfile()
|
||||
{
|
||||
DebugTools.Assert(EditedProfile != null);
|
||||
|
||||
@@ -720,8 +720,17 @@ namespace Content.Client.Lobby.UI
|
||||
_jobPriorities.Clear();
|
||||
var firstCategory = true;
|
||||
|
||||
var departments = _prototypeManager.EnumeratePrototypes<DepartmentPrototype>().ToArray();
|
||||
Array.Sort(departments, DepartmentUIComparer.Instance);
|
||||
// Get all displayed departments
|
||||
var departments = new List<DepartmentPrototype>();
|
||||
foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
|
||||
{
|
||||
if (department.EditorHidden)
|
||||
continue;
|
||||
|
||||
departments.Add(department);
|
||||
}
|
||||
|
||||
departments.Sort(DepartmentUIComparer.Instance);
|
||||
|
||||
var items = new[]
|
||||
{
|
||||
@@ -775,7 +784,7 @@ namespace Content.Client.Lobby.UI
|
||||
JobList.AddChild(category);
|
||||
}
|
||||
|
||||
var jobs = department.Roles.Select(jobId => _prototypeManager.Index<JobPrototype>(jobId))
|
||||
var jobs = department.Roles.Select(jobId => _prototypeManager.Index(jobId))
|
||||
.Where(job => job.SetPreference)
|
||||
.ToArray();
|
||||
|
||||
@@ -822,13 +831,15 @@ namespace Content.Client.Lobby.UI
|
||||
if (jobId == job.ID)
|
||||
{
|
||||
other.Select(selectedPrio);
|
||||
continue;
|
||||
}
|
||||
else if (selectedJobPrio == JobPriority.High && (JobPriority) other.Selected == JobPriority.High)
|
||||
{
|
||||
// Lower any other high priorities to medium.
|
||||
other.Select((int) JobPriority.Medium);
|
||||
Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium);
|
||||
}
|
||||
|
||||
if (selectedJobPrio != JobPriority.High || (JobPriority) other.Selected != JobPriority.High)
|
||||
continue;
|
||||
|
||||
// Lower any other high priorities to medium.
|
||||
other.Select((int)JobPriority.Medium);
|
||||
Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium);
|
||||
}
|
||||
|
||||
// TODO: Only reload on high change (either to or from).
|
||||
@@ -933,6 +944,11 @@ namespace Content.Client.Lobby.UI
|
||||
SetDirty();
|
||||
ReloadPreview();
|
||||
};
|
||||
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
UpdateJobPriorities();
|
||||
}
|
||||
|
||||
private void OnFlavorTextChange(string content)
|
||||
|
||||
@@ -101,7 +101,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
|
||||
if (!_mapManager.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||
return;
|
||||
|
||||
StartDragging(gridUid, Transform(gridUid).InvWorldMatrix.Transform(mousePos.Position));
|
||||
StartDragging(gridUid, Vector2.Transform(mousePos.Position, Transform(gridUid).InvWorldMatrix));
|
||||
}
|
||||
|
||||
if (!TryComp(_dragging, out TransformComponent? xform))
|
||||
@@ -116,7 +116,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var localToWorld = xform.WorldMatrix.Transform(_localPosition);
|
||||
var localToWorld = Vector2.Transform(_localPosition, xform.WorldMatrix);
|
||||
|
||||
if (localToWorld.EqualsApprox(mousePos.Position, 0.01f)) return;
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Content.Client.NPC
|
||||
|
||||
foreach (var crumb in chunk.Value)
|
||||
{
|
||||
var crumbMapPos = worldMatrix.Transform(_system.GetCoordinate(chunk.Key, crumb.Coordinates));
|
||||
var crumbMapPos = Vector2.Transform(_system.GetCoordinate(chunk.Key, crumb.Coordinates), worldMatrix);
|
||||
var distance = (crumbMapPos - mouseWorldPos.Position).Length();
|
||||
|
||||
if (distance < nearestDistance)
|
||||
@@ -292,7 +292,7 @@ namespace Content.Client.NPC
|
||||
|
||||
foreach (var poly in tile)
|
||||
{
|
||||
if (poly.Box.Contains(invGridMatrix.Transform(mouseWorldPos.Position)))
|
||||
if (poly.Box.Contains(Vector2.Transform(mouseWorldPos.Position, invGridMatrix)))
|
||||
{
|
||||
nearest = poly;
|
||||
break;
|
||||
@@ -488,7 +488,7 @@ namespace Content.Client.NPC
|
||||
if (neighborMap.MapId != args.MapId)
|
||||
continue;
|
||||
|
||||
neighborPos = invMatrix.Transform(neighborMap.Position);
|
||||
neighborPos = Vector2.Transform(neighborMap.Position, invMatrix);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -576,7 +576,7 @@ namespace Content.Client.NPC
|
||||
}
|
||||
}
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Content.Client.NodeContainer
|
||||
}
|
||||
|
||||
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
_gridIndex.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
|
||||
|
||||
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid,
|
||||
@@ -83,10 +83,10 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
continue;
|
||||
|
||||
var worldPosition = _transform.GetWorldPosition(xform);
|
||||
var worldMatrix = Matrix3.CreateTranslation(worldPosition);
|
||||
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition);
|
||||
|
||||
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
||||
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
||||
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
|
||||
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
|
||||
|
||||
handle.SetTransform(matty);
|
||||
|
||||
@@ -115,7 +115,7 @@ public sealed class EntityHealthBarOverlay : Overlay
|
||||
handle.DrawRect(pixelDarken, Black.WithAlpha(128));
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Client.Overlays;
|
||||
|
||||
public sealed partial class StencilOverlay
|
||||
{
|
||||
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3 invMatrix)
|
||||
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3x2 invMatrix)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var renderScale = args.Viewport.RenderScale.X;
|
||||
@@ -16,7 +16,7 @@ public sealed partial class StencilOverlay
|
||||
var length = zoom.X;
|
||||
var bufferRange = MathF.Min(10f, rangeComp.Range);
|
||||
|
||||
var pixelCenter = invMatrix.Transform(rangeComp.Origin);
|
||||
var pixelCenter = Vector2.Transform(rangeComp.Origin, invMatrix);
|
||||
// Something something offset?
|
||||
var vertical = args.Viewport.Size.Y;
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed partial class StencilOverlay
|
||||
worldHandle.DrawRect(localAABB, Color.White);
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
|
||||
@@ -10,7 +10,7 @@ public sealed partial class StencilOverlay
|
||||
{
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3 invMatrix)
|
||||
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3x2 invMatrix)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var mapId = args.MapId;
|
||||
@@ -32,7 +32,7 @@ public sealed partial class StencilOverlay
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var matrix = _transform.GetWorldMatrix(grid, xformQuery);
|
||||
Matrix3.Multiply(in matrix, in invMatrix, out var matty);
|
||||
var matty = Matrix3x2.Multiply(matrix, invMatrix);
|
||||
worldHandle.SetTransform(matty);
|
||||
|
||||
foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB))
|
||||
@@ -52,7 +52,7 @@ public sealed partial class StencilOverlay
|
||||
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
@@ -62,7 +62,7 @@ public sealed partial class StencilOverlay
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Parallax;
|
||||
using Content.Client.Weather;
|
||||
using Content.Shared.Salvage;
|
||||
@@ -72,6 +73,6 @@ public sealed partial class StencilOverlay : Overlay
|
||||
}
|
||||
|
||||
args.WorldHandle.UseShader(null);
|
||||
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public sealed partial class StampLabel : Label
|
||||
base.Draw(handle);
|
||||
|
||||
// Restore a sane transform+shader
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
handle.UseShader(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public sealed partial class StampWidget : PanelContainer
|
||||
base.Draw(handle);
|
||||
|
||||
// Restore a sane transform+shader
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
handle.UseShader(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ public partial class NavMapControl : MapGridControl
|
||||
|
||||
// Convert to a world position
|
||||
var unscaledPosition = (localPosition - MidPointVector) / MinimapScale;
|
||||
var worldPosition = _transformSystem.GetWorldMatrix(_xform).Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset);
|
||||
var worldPosition = Vector2.Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset, _transformSystem.GetWorldMatrix(_xform));
|
||||
|
||||
// Find closest tracked entity in range
|
||||
var closestEntity = NetEntity.Invalid;
|
||||
@@ -401,7 +401,7 @@ public partial class NavMapControl : MapGridControl
|
||||
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
{
|
||||
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
||||
var position = Vector2.Transform(mapPos.Position, _transformSystem.GetInvWorldMatrix(_xform)) - offset;
|
||||
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||
|
||||
handle.DrawCircle(position, float.Sqrt(MinimapScale) * 2f, value.Color);
|
||||
@@ -422,7 +422,7 @@ public partial class NavMapControl : MapGridControl
|
||||
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
{
|
||||
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
||||
var position = Vector2.Transform(mapPos.Position, _transformSystem.GetInvWorldMatrix(_xform)) - offset;
|
||||
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||
|
||||
var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Players.JobWhitelist;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client;
|
||||
@@ -24,6 +25,7 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
|
||||
private readonly Dictionary<string, TimeSpan> _roles = new();
|
||||
private readonly List<string> _roleBans = new();
|
||||
private readonly List<string> _jobWhitelists = new();
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
@@ -36,6 +38,7 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
// Yeah the client manager handles role bans and playtime but the server ones are separate DEAL.
|
||||
_net.RegisterNetMessage<MsgRoleBans>(RxRoleBans);
|
||||
_net.RegisterNetMessage<MsgPlayTime>(RxPlayTime);
|
||||
_net.RegisterNetMessage<MsgJobWhitelist>(RxJobWhitelist);
|
||||
|
||||
_client.RunLevelChanged += ClientOnRunLevelChanged;
|
||||
}
|
||||
@@ -79,6 +82,13 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
Updated?.Invoke();
|
||||
}
|
||||
|
||||
private void RxJobWhitelist(MsgJobWhitelist message)
|
||||
{
|
||||
_jobWhitelists.Clear();
|
||||
_jobWhitelists.AddRange(message.Whitelist);
|
||||
Updated?.Invoke();
|
||||
}
|
||||
|
||||
public bool IsAllowed(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
reason = null;
|
||||
@@ -89,6 +99,9 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckWhitelist(job, out reason))
|
||||
return false;
|
||||
|
||||
var player = _playerManager.LocalSession;
|
||||
if (player == null)
|
||||
return true;
|
||||
@@ -116,6 +129,21 @@ public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
return reason == null;
|
||||
}
|
||||
|
||||
public bool CheckWhitelist(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
reason = default;
|
||||
if (!_cfg.GetCVar(CCVars.GameRoleWhitelist))
|
||||
return true;
|
||||
|
||||
if (job.Whitelisted && !_jobWhitelists.Contains(job.ID))
|
||||
{
|
||||
reason = FormattedMessage.FromUnformatted(Loc.GetString("role-not-whitelisted"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public TimeSpan FetchOverallPlaytime()
|
||||
{
|
||||
return _roles.TryGetValue("Overall", out var overallPlaytime) ? overallPlaytime : TimeSpan.Zero;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
@@ -55,7 +56,7 @@ public sealed class PopupOverlay : Overlay
|
||||
if (args.ViewportControl == null)
|
||||
return;
|
||||
|
||||
args.DrawingHandle.SetTransform(Matrix3.Identity);
|
||||
args.DrawingHandle.SetTransform(Matrix3x2.Identity);
|
||||
args.DrawingHandle.UseShader(_shader);
|
||||
var scale = _configManager.GetCVar(CVars.DisplayUIScale);
|
||||
|
||||
@@ -90,7 +91,7 @@ public sealed class PopupOverlay : Overlay
|
||||
e => e == popup.InitialPos.EntityId || e == ourEntity, entMan: _entManager))
|
||||
continue;
|
||||
|
||||
var pos = matrix.Transform(mapPos.Position);
|
||||
var pos = Vector2.Transform(mapPos.Position, matrix);
|
||||
_controller.DrawPopup(popup, worldHandle, pos, scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,8 @@ public sealed partial class PowerMonitoringWindow
|
||||
button.ToolTip = Loc.GetString(name);
|
||||
|
||||
// Update power value
|
||||
button.PowerValue.Text = Loc.GetString("power-monitoring-window-value", ("value", entry.PowerValue));
|
||||
// Don't use SI prefixes, just give the number in W, so that it is readily apparent which consumer is using a lot of power.
|
||||
button.PowerValue.Text = Loc.GetString("power-monitoring-window-button-value", ("value", Math.Round(entry.PowerValue).ToString("N0")));
|
||||
}
|
||||
|
||||
private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContainer currentContainer, PowerMonitoringConsoleEntry[]? entries, SpriteSpecifier.Texture icon)
|
||||
@@ -480,6 +481,7 @@ public sealed class PowerMonitoringButton : Button
|
||||
PowerValue = new Label()
|
||||
{
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
Align = Label.AlignMode.Right,
|
||||
SetWidth = 72f,
|
||||
Margin = new Thickness(10, 0, 0, 0),
|
||||
ClipText = true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Shuttles.Events;
|
||||
using Content.Shared.Shuttles.Systems;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -75,6 +76,6 @@ public sealed class EmergencyShuttleOverlay : Overlay
|
||||
|
||||
args.WorldHandle.SetTransform(xform.WorldMatrix);
|
||||
args.WorldHandle.DrawRect(Position.Value, Color.Red.WithAlpha(100));
|
||||
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Shuttles.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -115,7 +116,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
{
|
||||
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
|
||||
var minimapScale = MinimapScale;
|
||||
@@ -289,7 +290,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
|
||||
public float MinimapScale;
|
||||
public Vector2 MidPoint;
|
||||
public Matrix3 Matrix;
|
||||
public Matrix3x2 Matrix;
|
||||
|
||||
public List<Vector2> Vertices;
|
||||
public Vector2[] ScaledVertices;
|
||||
@@ -297,7 +298,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
public void Execute(int index)
|
||||
{
|
||||
var vert = Vertices[index];
|
||||
var adjustedVert = Matrix.Transform(vert);
|
||||
var adjustedVert = Vector2.Transform(vert, Matrix);
|
||||
adjustedVert = adjustedVert with { Y = -adjustedVert.Y };
|
||||
|
||||
var scaledVert = ScalePosition(adjustedVert, MinimapScale, MidPoint);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Shuttles.BUIStates;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -68,7 +69,7 @@ public sealed partial class NavScreen : BoxContainer
|
||||
}
|
||||
|
||||
var (_, worldRot, worldMatrix) = _xformSystem.GetWorldPositionRotationMatrix(gridXform);
|
||||
var worldPos = worldMatrix.Transform(gridBody.LocalCenter);
|
||||
var worldPos = Vector2.Transform(gridBody.LocalCenter, worldMatrix);
|
||||
|
||||
// Get the positive reduced angle.
|
||||
var displayRot = -worldRot.Reduced();
|
||||
|
||||
@@ -108,10 +108,10 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||
var gridNent = EntManager.GetNetEntity(GridEntity);
|
||||
var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value);
|
||||
var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner);
|
||||
var dockMatrix = Matrix3.CreateTransform(_coordinates.Value.Position, Angle.Zero);
|
||||
Matrix3.Multiply(dockMatrix, ourGridMatrix, out var offsetMatrix);
|
||||
var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero);
|
||||
var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix);
|
||||
|
||||
offsetMatrix = offsetMatrix.Invert();
|
||||
Matrix3x2.Invert(worldFromDock, out var offsetMatrix);
|
||||
|
||||
// Draw nearby grids
|
||||
var controlBounds = PixelSizeBox;
|
||||
@@ -137,7 +137,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||
continue;
|
||||
|
||||
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||
Matrix3.Multiply(in gridMatrix, in offsetMatrix, out var matty);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, offsetMatrix);
|
||||
var color = _shuttles.GetIFFColor(grid.Owner, grid.Owner == GridEntity, component: iffComp);
|
||||
|
||||
DrawGrid(handle, matty, grid, color);
|
||||
@@ -151,23 +151,23 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||
if (ViewedDock == dock.Entity)
|
||||
continue;
|
||||
|
||||
var position = matty.Transform(dock.Coordinates.Position);
|
||||
var position = Vector2.Transform(dock.Coordinates.Position, matty);
|
||||
|
||||
var otherDockRotation = Matrix3.CreateRotation(dock.Angle);
|
||||
var otherDockRotation = Matrix3Helpers.CreateRotation(dock.Angle);
|
||||
var scaledPos = ScalePosition(position with {Y = -position.Y});
|
||||
|
||||
if (!controlBounds.Contains(scaledPos.Floored()))
|
||||
continue;
|
||||
|
||||
// Draw the dock's collision
|
||||
var collisionBL = matty.Transform(dock.Coordinates.Position +
|
||||
otherDockRotation.Transform(new Vector2(-0.2f, -0.7f)));
|
||||
var collisionBR = matty.Transform(dock.Coordinates.Position +
|
||||
otherDockRotation.Transform(new Vector2(0.2f, -0.7f)));
|
||||
var collisionTR = matty.Transform(dock.Coordinates.Position +
|
||||
otherDockRotation.Transform(new Vector2(0.2f, -0.5f)));
|
||||
var collisionTL = matty.Transform(dock.Coordinates.Position +
|
||||
otherDockRotation.Transform(new Vector2(-0.2f, -0.5f)));
|
||||
var collisionBL = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(-0.2f, -0.7f), otherDockRotation), matty);
|
||||
var collisionBR = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(0.2f, -0.7f), otherDockRotation), matty);
|
||||
var collisionTR = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(0.2f, -0.5f), otherDockRotation), matty);
|
||||
var collisionTL = Vector2.Transform(dock.Coordinates.Position +
|
||||
Vector2.Transform(new Vector2(-0.2f, -0.5f), otherDockRotation), matty);
|
||||
|
||||
var verts = new[]
|
||||
{
|
||||
@@ -195,10 +195,10 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts, otherDockConnection);
|
||||
|
||||
// Draw the dock itself
|
||||
var dockBL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f));
|
||||
var dockBR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f));
|
||||
var dockTR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f));
|
||||
var dockTL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f));
|
||||
var dockBL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f), matty);
|
||||
var dockBR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f), matty);
|
||||
var dockTR = Vector2.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f), matty);
|
||||
var dockTL = Vector2.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f), matty);
|
||||
|
||||
verts = new[]
|
||||
{
|
||||
@@ -308,14 +308,14 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||
// Draw the dock's collision
|
||||
var invertedPosition = Vector2.Zero;
|
||||
invertedPosition.Y = -invertedPosition.Y;
|
||||
var rotation = Matrix3.CreateRotation(-_angle.Value + MathF.PI);
|
||||
var rotation = Matrix3Helpers.CreateRotation(-_angle.Value + MathF.PI);
|
||||
var ourDockConnection = new UIBox2(
|
||||
ScalePosition(rotation.Transform(new Vector2(-0.2f, -0.7f))),
|
||||
ScalePosition(rotation.Transform(new Vector2(0.2f, -0.5f))));
|
||||
ScalePosition(Vector2.Transform(new Vector2(-0.2f, -0.7f), rotation)),
|
||||
ScalePosition(Vector2.Transform(new Vector2(0.2f, -0.5f), rotation)));
|
||||
|
||||
var ourDock = new UIBox2(
|
||||
ScalePosition(rotation.Transform(new Vector2(-0.5f, 0.5f))),
|
||||
ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f))));
|
||||
ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)),
|
||||
ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation)));
|
||||
|
||||
var dockColor = Color.Magenta;
|
||||
var connectionColor = Color.Pink;
|
||||
|
||||
@@ -114,7 +114,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
var beaconsOnly = EntManager.TryGetComponent(mapUid, out FTLDestinationComponent? destComp) &&
|
||||
destComp.BeaconsOnly;
|
||||
|
||||
var mapTransform = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
|
||||
var mapTransform = Matrix3Helpers.CreateInverseTransform(Offset, Angle.Zero);
|
||||
|
||||
if (beaconsOnly && TryGetBeacon(_beacons, mapTransform, args.RelativePixelPosition, PixelRect, out var foundBeacon, out _))
|
||||
{
|
||||
@@ -203,7 +203,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
/// </summary>
|
||||
/// <param name="mapObjects"></param>
|
||||
/// <returns></returns>
|
||||
private List<IMapObject> GetViewportMapObjects(Matrix3 matty, List<IMapObject> mapObjects)
|
||||
private List<IMapObject> GetViewportMapObjects(Matrix3x2 matty, List<IMapObject> mapObjects)
|
||||
{
|
||||
var results = new List<IMapObject>();
|
||||
var enlargement = new Vector2i((int) (16 * UIScale), (int) (16 * UIScale));
|
||||
@@ -217,7 +217,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
|
||||
var mapCoords = _shuttles.GetMapCoordinates(mapObj);
|
||||
|
||||
var relativePos = matty.Transform(mapCoords.Position);
|
||||
var relativePos = Vector2.Transform(mapCoords.Position, matty);
|
||||
relativePos = relativePos with { Y = -relativePos.Y };
|
||||
var uiPosition = ScalePosition(relativePos);
|
||||
|
||||
@@ -250,7 +250,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
DrawParallax(handle);
|
||||
|
||||
var viewedMapUid = _mapManager.GetMapEntityId(ViewingMap);
|
||||
var matty = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
|
||||
var matty = Matrix3Helpers.CreateInverseTransform(Offset, Angle.Zero);
|
||||
var realTime = _timing.RealTime;
|
||||
var viewBox = new Box2(Offset - WorldRangeVector, Offset + WorldRangeVector);
|
||||
var viewportObjects = GetViewportMapObjects(matty, mapObjects);
|
||||
@@ -267,7 +267,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(shuttleXform);
|
||||
gridPos = Maps.GetGridPosition((gridUid, gridPhysics), gridPos, gridRot);
|
||||
|
||||
var gridRelativePos = matty.Transform(gridPos);
|
||||
var gridRelativePos = Vector2.Transform(gridPos, matty);
|
||||
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
|
||||
var gridUiPos = ScalePosition(gridRelativePos);
|
||||
|
||||
@@ -296,7 +296,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
continue;
|
||||
}
|
||||
|
||||
var adjustedPos = matty.Transform(mapCoords.Position);
|
||||
var adjustedPos = Vector2.Transform(mapCoords.Position, matty);
|
||||
var localPos = ScalePosition(adjustedPos with { Y = -adjustedPos.Y});
|
||||
handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor.WithAlpha(0.05f));
|
||||
handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor, filled: false);
|
||||
@@ -319,7 +319,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
|
||||
foreach (var (beaconName, coords, mapO) in GetBeacons(viewportObjects, matty, controlLocalBounds))
|
||||
{
|
||||
var localPos = matty.Transform(coords.Position);
|
||||
var localPos = Vector2.Transform(coords.Position, matty);
|
||||
localPos = localPos with { Y = -localPos.Y };
|
||||
var beaconUiPos = ScalePosition(localPos);
|
||||
var mapObject = GetMapObject(localPos, Angle.Zero, scale: 0.75f, scalePosition: true);
|
||||
@@ -360,7 +360,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(grid.Owner);
|
||||
gridPos = Maps.GetGridPosition((grid, gridPhysics), gridPos, gridRot);
|
||||
|
||||
var gridRelativePos = matty.Transform(gridPos);
|
||||
var gridRelativePos = Vector2.Transform(gridPos, matty);
|
||||
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
|
||||
var gridUiPos = ScalePosition(gridRelativePos);
|
||||
|
||||
@@ -439,7 +439,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
|
||||
var color = ftlFree ? Color.LimeGreen : Color.Magenta;
|
||||
|
||||
var gridRelativePos = matty.Transform(gridPos);
|
||||
var gridRelativePos = Vector2.Transform(gridPos, matty);
|
||||
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
|
||||
var gridUiPos = ScalePosition(gridRelativePos);
|
||||
|
||||
@@ -512,7 +512,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
/// <summary>
|
||||
/// Returns the beacons that intersect the viewport.
|
||||
/// </summary>
|
||||
private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List<IMapObject> mapObjs, Matrix3 mapTransform, UIBox2i area)
|
||||
private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List<IMapObject> mapObjs, Matrix3x2 mapTransform, UIBox2i area)
|
||||
{
|
||||
foreach (var mapO in mapObjs)
|
||||
{
|
||||
@@ -520,7 +520,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
continue;
|
||||
|
||||
var beaconCoords = EntManager.GetCoordinates(beacon.Coordinates).ToMap(EntManager, _xformSystem);
|
||||
var position = mapTransform.Transform(beaconCoords.Position);
|
||||
var position = Vector2.Transform(beaconCoords.Position, mapTransform);
|
||||
var localPos = ScalePosition(position with {Y = -position.Y});
|
||||
|
||||
// If beacon not on screen then ignore it.
|
||||
@@ -557,7 +557,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
return mapObj;
|
||||
}
|
||||
|
||||
private bool TryGetBeacon(IEnumerable<IMapObject> mapObjects, Matrix3 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos)
|
||||
private bool TryGetBeacon(IEnumerable<IMapObject> mapObjects, Matrix3x2 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos)
|
||||
{
|
||||
// In pixels
|
||||
const float BeaconSnapRange = 32f;
|
||||
@@ -579,7 +579,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||
if (!_shuttles.CanFTLBeacon(beaconObj.Coordinates))
|
||||
continue;
|
||||
|
||||
var position = mapTransform.Transform(beaconCoords.Position);
|
||||
var position = Vector2.Transform(beaconCoords.Position, mapTransform);
|
||||
var localPos = ScalePosition(position with {Y = -position.Y});
|
||||
|
||||
// If beacon not on screen then ignore it.
|
||||
|
||||
@@ -137,10 +137,10 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
|
||||
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
|
||||
var offset = _coordinates.Value.Position;
|
||||
var posMatrix = Matrix3.CreateTransform(offset, _rotation.Value);
|
||||
var posMatrix = Matrix3Helpers.CreateTransform(offset, _rotation.Value);
|
||||
var (_, ourEntRot, ourEntMatrix) = _transform.GetWorldPositionRotationMatrix(_coordinates.Value.EntityId);
|
||||
Matrix3.Multiply(posMatrix, ourEntMatrix, out var ourWorldMatrix);
|
||||
var ourWorldMatrixInvert = ourWorldMatrix.Invert();
|
||||
var ourWorldMatrix = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
|
||||
Matrix3x2.Invert(ourWorldMatrix, out var ourWorldMatrixInvert);
|
||||
|
||||
// Draw our grid in detail
|
||||
var ourGridId = xform.GridUid;
|
||||
@@ -148,7 +148,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
fixturesQuery.HasComponent(ourGridId.Value))
|
||||
{
|
||||
var ourGridMatrix = _transform.GetWorldMatrix(ourGridId.Value);
|
||||
Matrix3.Multiply(in ourGridMatrix, in ourWorldMatrixInvert, out var matrix);
|
||||
var matrix = Matrix3x2.Multiply(ourGridMatrix, ourWorldMatrixInvert);
|
||||
var color = _shuttles.GetIFFColor(ourGridId.Value, self: true);
|
||||
|
||||
DrawGrid(handle, matrix, (ourGridId.Value, ourGrid), color);
|
||||
@@ -194,7 +194,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
continue;
|
||||
|
||||
var gridMatrix = _transform.GetWorldMatrix(gUid);
|
||||
Matrix3.Multiply(in gridMatrix, in ourWorldMatrixInvert, out var matty);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, ourWorldMatrixInvert);
|
||||
var color = _shuttles.GetIFFColor(grid, self: false, iff);
|
||||
|
||||
// Others default:
|
||||
@@ -207,7 +207,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
{
|
||||
var gridBounds = grid.Comp.LocalAABB;
|
||||
|
||||
var gridCentre = matty.Transform(gridBody.LocalCenter);
|
||||
var gridCentre = Vector2.Transform(gridBody.LocalCenter, matty);
|
||||
gridCentre.Y = -gridCentre.Y;
|
||||
var distance = gridCentre.Length();
|
||||
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName),
|
||||
@@ -242,7 +242,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix)
|
||||
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3x2 matrix)
|
||||
{
|
||||
if (!ShowDocks)
|
||||
return;
|
||||
@@ -255,7 +255,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
foreach (var state in docks)
|
||||
{
|
||||
var position = state.Coordinates.Position;
|
||||
var uiPosition = matrix.Transform(position);
|
||||
var uiPosition = Vector2.Transform(position, matrix);
|
||||
|
||||
if (uiPosition.Length() > (WorldRange * 2f) - DockScale)
|
||||
continue;
|
||||
@@ -264,10 +264,10 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||
|
||||
var verts = new[]
|
||||
{
|
||||
matrix.Transform(position + new Vector2(-DockScale, -DockScale)),
|
||||
matrix.Transform(position + new Vector2(DockScale, -DockScale)),
|
||||
matrix.Transform(position + new Vector2(DockScale, DockScale)),
|
||||
matrix.Transform(position + new Vector2(-DockScale, DockScale)),
|
||||
Vector2.Transform(position + new Vector2(-DockScale, -DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(DockScale, -DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(DockScale, DockScale), matrix),
|
||||
Vector2.Transform(position + new Vector2(-DockScale, DockScale), matrix),
|
||||
};
|
||||
|
||||
for (var i = 0; i < verts.Length; i++)
|
||||
|
||||
@@ -39,8 +39,8 @@ public sealed class StatusIconOverlay : Overlay
|
||||
var eyeRot = args.Viewport.Eye?.Rotation ?? default;
|
||||
|
||||
var xformQuery = _entity.GetEntityQuery<TransformComponent>();
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(1, 1));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-eyeRot);
|
||||
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(1, 1));
|
||||
var rotationMatrix = Matrix3Helpers.CreateRotation(-eyeRot);
|
||||
|
||||
var query = _entity.AllEntityQueryEnumerator<StatusIconComponent, SpriteComponent, TransformComponent, MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var sprite, out var xform, out var meta))
|
||||
@@ -59,9 +59,9 @@ public sealed class StatusIconOverlay : Overlay
|
||||
if (icons.Count == 0)
|
||||
continue;
|
||||
|
||||
var worldMatrix = Matrix3.CreateTranslation(worldPos);
|
||||
Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
|
||||
Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
|
||||
var worldMatrix = Matrix3Helpers.CreateTranslation(worldPos);
|
||||
var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix);
|
||||
var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld);
|
||||
handle.SetTransform(matty);
|
||||
|
||||
var countL = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Animations;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Storage;
|
||||
@@ -149,7 +150,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
||||
}
|
||||
|
||||
var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem);
|
||||
var finalPos = TransformSystem.GetInvWorldMatrix(initialCoords.EntityId).Transform(finalMapPos);
|
||||
var finalPos = Vector2.Transform(finalMapPos, TransformSystem.GetInvWorldMatrix(initialCoords.EntityId));
|
||||
|
||||
_entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Store;
|
||||
using JetBrains.Annotations;
|
||||
using System.Linq;
|
||||
using Content.Shared.Store.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Store.Ui;
|
||||
@@ -13,9 +14,6 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
||||
[ViewVariables]
|
||||
private StoreMenu? _menu;
|
||||
|
||||
[ViewVariables]
|
||||
private string _windowName = Loc.GetString("store-ui-default-title");
|
||||
|
||||
[ViewVariables]
|
||||
private string _search = string.Empty;
|
||||
|
||||
@@ -28,7 +26,9 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
_menu = new StoreMenu(_windowName);
|
||||
_menu = new StoreMenu();
|
||||
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
|
||||
_menu.Title = Loc.GetString(store.Name);
|
||||
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
@@ -64,25 +64,15 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (_menu == null)
|
||||
return;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case StoreUpdateState msg:
|
||||
_listings = msg.Listings;
|
||||
|
||||
_menu.UpdateBalance(msg.Balance);
|
||||
_menu?.UpdateBalance(msg.Balance);
|
||||
UpdateListingsWithSearchFilter();
|
||||
_menu.SetFooterVisibility(msg.ShowFooter);
|
||||
_menu.UpdateRefund(msg.AllowRefund);
|
||||
break;
|
||||
case StoreInitializeState msg:
|
||||
_windowName = msg.Name;
|
||||
if (_menu != null && _menu.Window != null)
|
||||
{
|
||||
_menu.Window.Title = msg.Name;
|
||||
}
|
||||
_menu?.SetFooterVisibility(msg.ShowFooter);
|
||||
_menu?.UpdateRefund(msg.AllowRefund);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed partial class StoreMenu : DefaultWindow
|
||||
|
||||
private List<ListingData> _cachedListings = new();
|
||||
|
||||
public StoreMenu(string name)
|
||||
public StoreMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -40,9 +40,6 @@ public sealed partial class StoreMenu : DefaultWindow
|
||||
WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
|
||||
RefundButton.OnButtonDown += OnRefundButtonDown;
|
||||
SearchBar.OnTextChanged += _ => SearchTextUpdated?.Invoke(this, SearchBar.Text);
|
||||
|
||||
if (Window != null)
|
||||
Window.Title = name;
|
||||
}
|
||||
|
||||
public void UpdateBalance(Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> balance)
|
||||
|
||||
@@ -65,7 +65,7 @@ public sealed class DirectionIcon : TextureRect
|
||||
if (_rotation != null)
|
||||
{
|
||||
var offset = (-_rotation.Value).RotateVec(Size * UIScale / 2) - Size * UIScale / 2;
|
||||
handle.SetTransform(Matrix3.CreateTransform(GlobalPixelPosition - offset, -_rotation.Value));
|
||||
handle.SetTransform(Matrix3Helpers.CreateTransform(GlobalPixelPosition - offset, -_rotation.Value));
|
||||
}
|
||||
|
||||
base.Draw(handle);
|
||||
|
||||
@@ -8,7 +8,8 @@ using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls;
|
||||
|
||||
public sealed class ListContainer : Control
|
||||
[Virtual]
|
||||
public class ListContainer : Control
|
||||
{
|
||||
public const string StylePropertySeparation = "separation";
|
||||
public const string StyleClassListContainerButton = "list-container-button";
|
||||
@@ -21,9 +22,26 @@ public sealed class ListContainer : Control
|
||||
set => _buttonGroup = value ? new ButtonGroup() : null;
|
||||
}
|
||||
public bool Toggle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when creating a button on the UI.
|
||||
/// The provided <see cref="ListContainerButton"/> is the generated button that Controls should be parented to.
|
||||
/// </summary>
|
||||
public Action<ListData, ListContainerButton>? GenerateItem;
|
||||
public Action<BaseButton.ButtonEventArgs?, ListData?>? ItemPressed;
|
||||
public Action<GUIBoundKeyEventArgs, ListData?>? ItemKeyBindDown;
|
||||
|
||||
/// <inheritdoc cref="BaseButton.OnPressed"/>
|
||||
public Action<BaseButton.ButtonEventArgs, ListData>? ItemPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a KeyBind is pressed on a ListContainerButton.
|
||||
/// </summary>
|
||||
public Action<GUIBoundKeyEventArgs, ListData>? ItemKeyBindDown;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the selected item does not exist in the new data when PopulateList is called.
|
||||
/// </summary>
|
||||
public Action? NoItemSelected;
|
||||
|
||||
public IReadOnlyList<ListData> Data => _data;
|
||||
|
||||
private const int DefaultSeparation = 3;
|
||||
@@ -72,11 +90,11 @@ public sealed class ListContainer : Control
|
||||
_vScrollBar.OnValueChanged += ScrollValueChanged;
|
||||
}
|
||||
|
||||
public void PopulateList(IReadOnlyList<ListData> data)
|
||||
public virtual void PopulateList(IReadOnlyList<ListData> data)
|
||||
{
|
||||
if ((_itemHeight == 0 || _data is {Count: 0}) && data.Count > 0)
|
||||
{
|
||||
ListContainerButton control = new(data[0]);
|
||||
ListContainerButton control = new(data[0], 0);
|
||||
GenerateItem?.Invoke(data[0], control);
|
||||
control.Measure(Vector2Helpers.Infinity);
|
||||
_itemHeight = control.DesiredSize.Y;
|
||||
@@ -97,7 +115,7 @@ public sealed class ListContainer : Control
|
||||
if (_selected != null && !data.Contains(_selected))
|
||||
{
|
||||
_selected = null;
|
||||
ItemPressed?.Invoke(null, null);
|
||||
NoItemSelected?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +134,7 @@ public sealed class ListContainer : Control
|
||||
if (_buttons.TryGetValue(data, out var button) && Toggle)
|
||||
button.Pressed = true;
|
||||
_selected = data;
|
||||
button ??= new ListContainerButton(data);
|
||||
button ??= new ListContainerButton(data, _data.IndexOf(data));
|
||||
OnItemPressed(new BaseButton.ButtonEventArgs(button,
|
||||
new GUIBoundKeyEventArgs(EngineKeyFunctions.UIClick, BoundKeyState.Up,
|
||||
new ScreenCoordinates(0, 0, WindowId.Main), true, Vector2.Zero, Vector2.Zero)));
|
||||
@@ -260,7 +278,7 @@ public sealed class ListContainer : Control
|
||||
toRemove.Remove(data);
|
||||
else
|
||||
{
|
||||
button = new ListContainerButton(data);
|
||||
button = new ListContainerButton(data, i);
|
||||
button.OnPressed += OnItemPressed;
|
||||
button.OnKeyBindDown += args => OnItemKeyBindDown(button, args);
|
||||
button.ToggleMode = Toggle;
|
||||
@@ -360,11 +378,14 @@ public sealed class ListContainer : Control
|
||||
public sealed class ListContainerButton : ContainerButton, IEntityControl
|
||||
{
|
||||
public readonly ListData Data;
|
||||
|
||||
public readonly int Index;
|
||||
// public PanelContainer Background;
|
||||
|
||||
public ListContainerButton(ListData data)
|
||||
public ListContainerButton(ListData data, int index)
|
||||
{
|
||||
Data = data;
|
||||
Index = index;
|
||||
// AddChild(Background = new PanelContainer
|
||||
// {
|
||||
// HorizontalExpand = true,
|
||||
|
||||
@@ -169,7 +169,7 @@ public partial class MapGridControl : LayoutContainer
|
||||
var inversePos = (value - MidPointVector) / MinimapScale;
|
||||
|
||||
inversePos = inversePos with { Y = -inversePos.Y };
|
||||
inversePos = Matrix3.CreateTransform(Offset, Angle.Zero).Transform(inversePos);
|
||||
inversePos = Vector2.Transform(inversePos, Matrix3Helpers.CreateTransform(Offset, Angle.Zero));
|
||||
return inversePos;
|
||||
}
|
||||
|
||||
|
||||
68
Content.Client/UserInterface/Controls/SearchListContainer.cs
Normal file
68
Content.Client/UserInterface/Controls/SearchListContainer.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Linq;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls;
|
||||
|
||||
public sealed class SearchListContainer : ListContainer
|
||||
{
|
||||
private LineEdit? _searchBar;
|
||||
private List<ListData> _unfilteredData = new();
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="LineEdit"/> that is used to filter the list data.
|
||||
/// </summary>
|
||||
public LineEdit? SearchBar
|
||||
{
|
||||
get => _searchBar;
|
||||
set
|
||||
{
|
||||
if (_searchBar is not null)
|
||||
_searchBar.OnTextChanged -= FilterList;
|
||||
|
||||
_searchBar = value;
|
||||
|
||||
if (_searchBar is null)
|
||||
return;
|
||||
|
||||
_searchBar.OnTextChanged += FilterList;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs over the ListData to determine if it should pass the filter.
|
||||
/// </summary>
|
||||
public Func<string, ListData, bool>? DataFilterCondition = null;
|
||||
|
||||
public override void PopulateList(IReadOnlyList<ListData> data)
|
||||
{
|
||||
_unfilteredData = data.ToList();
|
||||
FilterList();
|
||||
}
|
||||
|
||||
private void FilterList(LineEdit.LineEditEventArgs obj)
|
||||
{
|
||||
FilterList();
|
||||
}
|
||||
|
||||
private void FilterList()
|
||||
{
|
||||
var filterText = SearchBar?.Text;
|
||||
|
||||
if (DataFilterCondition is null || string.IsNullOrEmpty(filterText))
|
||||
{
|
||||
base.PopulateList(_unfilteredData);
|
||||
return;
|
||||
}
|
||||
|
||||
var filteredData = new List<ListData>();
|
||||
foreach (var data in _unfilteredData)
|
||||
{
|
||||
if (!DataFilterCondition(filterText, data))
|
||||
continue;
|
||||
|
||||
filteredData.Add(data);
|
||||
}
|
||||
|
||||
base.PopulateList(filteredData);
|
||||
}
|
||||
}
|
||||
285
Content.Client/UserInterface/Controls/TableContainer.cs
Normal file
285
Content.Client/UserInterface/Controls/TableContainer.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls;
|
||||
|
||||
// This control is not part of engine because I quickly wrote it in 2 hours at 2 AM and don't want to deal with
|
||||
// API stabilization and/or figuring out relation to GridContainer.
|
||||
// Grid layout is a complicated problem and I don't want to commit another half-baked thing into the engine.
|
||||
// It's probably sufficient for its use case (RichTextLabel tables for rules/guidebook).
|
||||
// Despite that, it's still better comment the shit half of you write on a regular basis.
|
||||
//
|
||||
// EMO: thank you PJB i was going to kill myself.
|
||||
|
||||
/// <summary>
|
||||
/// Displays children in a tabular grid. Unlike <see cref="GridContainer"/>,
|
||||
/// properly handles layout constraints so putting word-wrapping <see cref="RichTextLabel"/> in it should work.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All children are automatically laid out in <see cref="Columns"/> columns.
|
||||
/// The first control is in the top left, laid out per row from there.
|
||||
/// </remarks>
|
||||
[Virtual]
|
||||
public class TableContainer : Container
|
||||
{
|
||||
private int _columns = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The absolute minimum width a column can be forced to.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a column *asks* for less width than this (small contents), it can still be smaller.
|
||||
/// But if it asks for more it cannot go below this width.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public float MinForcedColumnWidth { get; set; } = 50;
|
||||
|
||||
// Scratch space used while calculating layout, cached to avoid regular allocations during layout pass.
|
||||
private ColumnData[] _columnDataCache = [];
|
||||
private RowData[] _rowDataCache = [];
|
||||
|
||||
/// <summary>
|
||||
/// How many columns should be displayed.
|
||||
/// </summary>
|
||||
public int Columns
|
||||
{
|
||||
get => _columns;
|
||||
set
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1, nameof(value));
|
||||
|
||||
_columns = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
ResetCachedArrays();
|
||||
|
||||
// Do a first pass measuring all child controls as if they're given infinite space.
|
||||
// This gives us a maximum width the columns want, which we use to proportion them later.
|
||||
var columnIdx = 0;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
ref var column = ref _columnDataCache[columnIdx];
|
||||
|
||||
child.Measure(new Vector2(float.PositiveInfinity, float.PositiveInfinity));
|
||||
column.MaxWidth = Math.Max(column.MaxWidth, child.DesiredSize.X);
|
||||
|
||||
columnIdx += 1;
|
||||
if (columnIdx == _columns)
|
||||
columnIdx = 0;
|
||||
}
|
||||
|
||||
// Calculate Slack and MinWidth for all columns. Also calculate sums for all columns.
|
||||
var totalMinWidth = 0f;
|
||||
var totalMaxWidth = 0f;
|
||||
var totalSlack = 0f;
|
||||
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
column.MinWidth = Math.Min(column.MaxWidth, MinForcedColumnWidth);
|
||||
column.Slack = column.MaxWidth - column.MinWidth;
|
||||
|
||||
totalMinWidth += column.MinWidth;
|
||||
totalMaxWidth += column.MaxWidth;
|
||||
totalSlack += column.Slack;
|
||||
}
|
||||
|
||||
if (totalMaxWidth <= availableSize.X)
|
||||
{
|
||||
// We want less horizontal space than we're given. Huh, that's convenient.
|
||||
// Just set assigned width to be however much they asked for.
|
||||
// We could probably skip the second measure pass in this scenario,
|
||||
// but that's just an optimization, so I don't care right now.
|
||||
//
|
||||
// There's probably a very clever way to make this behavior work with the else block of logic,
|
||||
// just by fiddling with the math.
|
||||
// I'm dumb, it's 4:30 AM. Yeah, I *started* at 2 AM.
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
|
||||
column.AssignedWidth = column.MaxWidth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have enough horizontal space,
|
||||
// at least without causing *some* sort of word wrapping (assuming text contents).
|
||||
//
|
||||
// Assign horizontal space proportional to the wanted maximum size of the columns.
|
||||
var assignableWidth = Math.Max(0, availableSize.X - totalMinWidth);
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
|
||||
var slackRatio = column.Slack / totalSlack;
|
||||
column.AssignedWidth = column.MinWidth + slackRatio * assignableWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Go over controls for a second measuring pass, this time giving them their assigned measure width.
|
||||
// This will give us a height to slot into per-row data.
|
||||
// We still measure assuming infinite vertical space.
|
||||
// This control can't properly handle being constrained on the Y axis.
|
||||
columnIdx = 0;
|
||||
var rowIdx = 0;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
ref var column = ref _columnDataCache[columnIdx];
|
||||
ref var row = ref _rowDataCache[rowIdx];
|
||||
|
||||
child.Measure(new Vector2(column.AssignedWidth, float.PositiveInfinity));
|
||||
row.MeasuredHeight = Math.Max(row.MeasuredHeight, child.DesiredSize.Y);
|
||||
|
||||
columnIdx += 1;
|
||||
if (columnIdx == _columns)
|
||||
{
|
||||
columnIdx = 0;
|
||||
rowIdx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Sum up height of all rows to get final measured table height.
|
||||
var totalHeight = 0f;
|
||||
for (var r = 0; r < _rowDataCache.Length; r++)
|
||||
{
|
||||
ref var row = ref _rowDataCache[r];
|
||||
totalHeight += row.MeasuredHeight;
|
||||
}
|
||||
|
||||
return new Vector2(Math.Min(availableSize.X, totalMaxWidth), totalHeight);
|
||||
}
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
// TODO: Expand to fit given vertical space.
|
||||
|
||||
// Calculate MinWidth and Slack sums again from column data.
|
||||
// We could've cached these from measure but whatever.
|
||||
var totalMinWidth = 0f;
|
||||
var totalSlack = 0f;
|
||||
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
totalMinWidth += column.MinWidth;
|
||||
totalSlack += column.Slack;
|
||||
}
|
||||
|
||||
// Calculate new width based on final given size, also assign horizontal positions of all columns.
|
||||
var assignableWidth = Math.Max(0, finalSize.X - totalMinWidth);
|
||||
var xPos = 0f;
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
|
||||
var slackRatio = column.Slack / totalSlack;
|
||||
column.ArrangedWidth = column.MinWidth + slackRatio * assignableWidth;
|
||||
column.ArrangedX = xPos;
|
||||
|
||||
xPos += column.ArrangedWidth;
|
||||
}
|
||||
|
||||
// Do actual arrangement row-by-row.
|
||||
var arrangeY = 0f;
|
||||
for (var r = 0; r < _rowDataCache.Length; r++)
|
||||
{
|
||||
ref var row = ref _rowDataCache[r];
|
||||
|
||||
for (var c = 0; c < _columns; c++)
|
||||
{
|
||||
ref var column = ref _columnDataCache[c];
|
||||
var index = c + r * _columns;
|
||||
|
||||
if (index >= ChildCount) // Quit early if we don't actually fill out the row.
|
||||
break;
|
||||
var child = GetChild(c + r * _columns);
|
||||
|
||||
child.Arrange(UIBox2.FromDimensions(column.ArrangedX, arrangeY, column.ArrangedWidth, row.MeasuredHeight));
|
||||
}
|
||||
|
||||
arrangeY += row.MeasuredHeight;
|
||||
}
|
||||
|
||||
return finalSize with { Y = arrangeY };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure cached array space is allocated to correct size and is reset to a clean slate.
|
||||
/// </summary>
|
||||
private void ResetCachedArrays()
|
||||
{
|
||||
// 1-argument Array.Clear() is not currently available in sandbox (added in .NET 6).
|
||||
|
||||
if (_columnDataCache.Length != _columns)
|
||||
_columnDataCache = new ColumnData[_columns];
|
||||
|
||||
Array.Clear(_columnDataCache, 0, _columnDataCache.Length);
|
||||
|
||||
var rowCount = ChildCount / _columns;
|
||||
if (ChildCount % _columns != 0)
|
||||
rowCount += 1;
|
||||
|
||||
if (rowCount != _rowDataCache.Length)
|
||||
_rowDataCache = new RowData[rowCount];
|
||||
|
||||
Array.Clear(_rowDataCache, 0, _rowDataCache.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-column data used during layout.
|
||||
/// </summary>
|
||||
private struct ColumnData
|
||||
{
|
||||
// Measure data.
|
||||
|
||||
/// <summary>
|
||||
/// The maximum width any control in this column wants, if given infinite space.
|
||||
/// Maximum of all controls on the column.
|
||||
/// </summary>
|
||||
public float MaxWidth;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum width this column may be given.
|
||||
/// This is either <see cref="MaxWidth"/> or <see cref="TableContainer.MinForcedColumnWidth"/>.
|
||||
/// </summary>
|
||||
public float MinWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Difference between max and min width; how much this column can expand from its minimum.
|
||||
/// </summary>
|
||||
public float Slack;
|
||||
|
||||
/// <summary>
|
||||
/// How much horizontal space this column was assigned at measure time.
|
||||
/// </summary>
|
||||
public float AssignedWidth;
|
||||
|
||||
// Arrange data.
|
||||
|
||||
/// <summary>
|
||||
/// How much horizontal space this column was assigned at arrange time.
|
||||
/// </summary>
|
||||
public float ArrangedWidth;
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal position this column was assigned at arrange time.
|
||||
/// </summary>
|
||||
public float ArrangedX;
|
||||
}
|
||||
|
||||
private struct RowData
|
||||
{
|
||||
// Measure data.
|
||||
|
||||
/// <summary>
|
||||
/// How much height the tallest control on this row was measured at,
|
||||
/// measuring for infinite vertical space but assigned column width.
|
||||
/// </summary>
|
||||
public float MeasuredHeight;
|
||||
}
|
||||
}
|
||||
@@ -177,12 +177,15 @@ public sealed class AdminUIController : UIController,
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayerTabEntryKeyBindDown(PlayerTabEntry entry, GUIBoundKeyEventArgs args)
|
||||
private void PlayerTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data)
|
||||
{
|
||||
if (entry.PlayerEntity == null)
|
||||
if (data is not PlayerListData {Info: var info})
|
||||
return;
|
||||
|
||||
var entity = entry.PlayerEntity.Value;
|
||||
if (info.NetEntity == null)
|
||||
return;
|
||||
|
||||
var entity = info.NetEntity.Value;
|
||||
var function = args.Function;
|
||||
|
||||
if (function == EngineKeyFunctions.UIClick)
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.Verbs;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -115,6 +116,13 @@ namespace Content.Client.Verbs.UI
|
||||
private void FillVerbPopup(ContextMenuPopup popup)
|
||||
{
|
||||
HashSet<string> listedCategories = new();
|
||||
var extras = new ValueList<string>(ExtraCategories.Count);
|
||||
|
||||
foreach (var cat in ExtraCategories)
|
||||
{
|
||||
extras.Add(cat.Text);
|
||||
}
|
||||
|
||||
foreach (var verb in CurrentVerbs)
|
||||
{
|
||||
if (verb.Category == null)
|
||||
@@ -122,17 +130,15 @@ namespace Content.Client.Verbs.UI
|
||||
var element = new VerbMenuElement(verb);
|
||||
_context.AddElement(popup, element);
|
||||
}
|
||||
else if (listedCategories.Add(verb.Category.Text))
|
||||
// Add the category if it's not an extra (this is to avoid shuffling if we're filling from server verbs response).
|
||||
else if (!extras.Contains(verb.Category.Text) && listedCategories.Add(verb.Category.Text))
|
||||
AddVerbCategory(verb.Category, popup);
|
||||
}
|
||||
|
||||
if (ExtraCategories != null)
|
||||
foreach (var category in ExtraCategories)
|
||||
{
|
||||
foreach (var category in ExtraCategories)
|
||||
{
|
||||
if (listedCategories.Add(category.Text))
|
||||
AddVerbCategory(category, popup);
|
||||
}
|
||||
if (listedCategories.Add(category.Text))
|
||||
AddVerbCategory(category, popup);
|
||||
}
|
||||
|
||||
popup.InvalidateMeasure();
|
||||
|
||||
@@ -123,7 +123,6 @@ namespace Content.Client.Verbs
|
||||
if ((visibility & MenuVisibility.Invisible) == 0)
|
||||
{
|
||||
var spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
var tagQuery = GetEntityQuery<TagComponent>();
|
||||
|
||||
for (var i = entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -131,7 +130,7 @@ namespace Content.Client.Verbs
|
||||
|
||||
if (!spriteQuery.TryGetComponent(entity, out var spriteComponent) ||
|
||||
!spriteComponent.Visible ||
|
||||
_tagSystem.HasTag(entity, "HideContextMenu", tagQuery))
|
||||
_tagSystem.HasTag(entity, "HideContextMenu"))
|
||||
{
|
||||
entities.RemoveSwap(i);
|
||||
}
|
||||
|
||||
@@ -277,8 +277,8 @@ namespace Content.Client.Viewport
|
||||
|
||||
EnsureViewportCreated();
|
||||
|
||||
var matrix = Matrix3.Invert(GetLocalToScreenMatrix());
|
||||
coords = matrix.Transform(coords);
|
||||
Matrix3x2.Invert(GetLocalToScreenMatrix(), out var matrix);
|
||||
coords = Vector2.Transform(coords, matrix);
|
||||
|
||||
return _viewport!.LocalToWorld(coords);
|
||||
}
|
||||
@@ -291,8 +291,8 @@ namespace Content.Client.Viewport
|
||||
|
||||
EnsureViewportCreated();
|
||||
|
||||
var matrix = Matrix3.Invert(GetLocalToScreenMatrix());
|
||||
coords = matrix.Transform(coords);
|
||||
Matrix3x2.Invert(GetLocalToScreenMatrix(), out var matrix);
|
||||
coords = Vector2.Transform(coords, matrix);
|
||||
|
||||
var ev = new PixelToMapEvent(coords, this, _viewport!);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, ref ev);
|
||||
@@ -311,16 +311,16 @@ namespace Content.Client.Viewport
|
||||
|
||||
var matrix = GetLocalToScreenMatrix();
|
||||
|
||||
return matrix.Transform(vpLocal);
|
||||
return Vector2.Transform(vpLocal, matrix);
|
||||
}
|
||||
|
||||
public Matrix3 GetWorldToScreenMatrix()
|
||||
public Matrix3x2 GetWorldToScreenMatrix()
|
||||
{
|
||||
EnsureViewportCreated();
|
||||
return _viewport!.GetWorldToLocalMatrix() * GetLocalToScreenMatrix();
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalToScreenMatrix()
|
||||
public Matrix3x2 GetLocalToScreenMatrix()
|
||||
{
|
||||
EnsureViewportCreated();
|
||||
|
||||
@@ -329,9 +329,9 @@ namespace Content.Client.Viewport
|
||||
|
||||
if (scaleFactor.X == 0 || scaleFactor.Y == 0)
|
||||
// Basically a nonsense scenario, at least make sure to return something that can be inverted.
|
||||
return Matrix3.Identity;
|
||||
return Matrix3x2.Identity;
|
||||
|
||||
return Matrix3.CreateTransform(GlobalPixelPosition + drawBox.TopLeft, 0, scaleFactor);
|
||||
return Matrix3Helpers.CreateTransform(GlobalPixelPosition + drawBox.TopLeft, 0, scaleFactor);
|
||||
}
|
||||
|
||||
private void EnsureViewportCreated()
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed partial class MeleeWeaponSystem
|
||||
case WeaponArcAnimation.None:
|
||||
var (mapPos, mapRot) = TransformSystem.GetWorldPositionRotation(userXform);
|
||||
var worldPos = mapPos + (mapRot - userXform.LocalRotation).RotateVec(localPos);
|
||||
var newLocalPos = TransformSystem.GetInvWorldMatrix(xform.ParentUid).Transform(worldPos);
|
||||
var newLocalPos = Vector2.Transform(worldPos, TransformSystem.GetInvWorldMatrix(xform.ParentUid));
|
||||
TransformSystem.SetLocalPositionNoLerp(animationUid, newLocalPos, xform);
|
||||
if (arcComponent.Fadeout)
|
||||
_animation.Play(animationUid, GetFadeAnimation(sprite, 0f, 0.15f), FadeAnimationKey);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using Content.Client.Resources;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -53,7 +53,7 @@ public abstract class BaseBulletRenderer : Control
|
||||
{
|
||||
// Scale rendering in this control by UIScale.
|
||||
var currentTransform = handle.GetTransform();
|
||||
handle.SetTransform(Matrix3.CreateScale(new Vector2(UIScale)) * currentTransform);
|
||||
handle.SetTransform(Matrix3Helpers.CreateScale(new Vector2(UIScale)) * currentTransform);
|
||||
|
||||
var countPerRow = CountPerRow(Size.X);
|
||||
|
||||
|
||||
69
Content.IntegrationTests/Pair/TestPair.Cvars.cs
Normal file
69
Content.IntegrationTests/Pair/TestPair.Cvars.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Pair;
|
||||
|
||||
public sealed partial class TestPair
|
||||
{
|
||||
private readonly Dictionary<string, object> _modifiedClientCvars = new();
|
||||
private readonly Dictionary<string, object> _modifiedServerCvars = new();
|
||||
|
||||
private void OnServerCvarChanged(CVarChangeInfo args)
|
||||
{
|
||||
_modifiedServerCvars.TryAdd(args.Name, args.OldValue);
|
||||
}
|
||||
|
||||
private void OnClientCvarChanged(CVarChangeInfo args)
|
||||
{
|
||||
_modifiedClientCvars.TryAdd(args.Name, args.OldValue);
|
||||
}
|
||||
|
||||
internal void ClearModifiedCvars()
|
||||
{
|
||||
_modifiedClientCvars.Clear();
|
||||
_modifiedServerCvars.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverts any cvars that were modified during a test back to their original values.
|
||||
/// </summary>
|
||||
public async Task RevertModifiedCvars()
|
||||
{
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
foreach (var (name, value) in _modifiedServerCvars)
|
||||
{
|
||||
if (Server.CfgMan.GetCVar(name).Equals(value))
|
||||
continue;
|
||||
Server.Log.Info($"Resetting cvar {name} to {value}");
|
||||
Server.CfgMan.SetCVar(name, value);
|
||||
}
|
||||
|
||||
// I just love order dependent cvars
|
||||
if (_modifiedServerCvars.TryGetValue(CCVars.PanicBunkerEnabled.Name, out var panik))
|
||||
Server.CfgMan.SetCVar(CCVars.PanicBunkerEnabled.Name, panik);
|
||||
|
||||
});
|
||||
|
||||
await Client.WaitPost(() =>
|
||||
{
|
||||
foreach (var (name, value) in _modifiedClientCvars)
|
||||
{
|
||||
if (Client.CfgMan.GetCVar(name).Equals(value))
|
||||
continue;
|
||||
|
||||
var flags = Client.CfgMan.GetCVarFlags(name);
|
||||
if (flags.HasFlag(CVar.REPLICATED) && flags.HasFlag(CVar.SERVER))
|
||||
continue;
|
||||
|
||||
Client.Log.Info($"Resetting cvar {name} to {value}");
|
||||
Client.CfgMan.SetCVar(name, value);
|
||||
}
|
||||
});
|
||||
|
||||
ClearModifiedCvars();
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,8 @@ public sealed partial class TestPair : IAsyncDisposable
|
||||
TestMap = null;
|
||||
}
|
||||
|
||||
await RevertModifiedCvars();
|
||||
|
||||
var usageTime = Watch.Elapsed;
|
||||
Watch.Restart();
|
||||
await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: Test borrowed pair {Id} for {usageTime.TotalMilliseconds} ms");
|
||||
@@ -132,6 +134,7 @@ public sealed partial class TestPair : IAsyncDisposable
|
||||
if (gameTicker.RunLevel != GameRunLevel.PreRoundLobby)
|
||||
{
|
||||
await testOut.WriteLineAsync($"Recycling: {Watch.Elapsed.TotalMilliseconds} ms: Restarting round.");
|
||||
Server.CfgMan.SetCVar(CCVars.GameDummyTicker, false);
|
||||
Assert.That(gameTicker.DummyTicker, Is.False);
|
||||
Server.CfgMan.SetCVar(CCVars.GameLobbyEnabled, true);
|
||||
await Server.WaitPost(() => gameTicker.RestartRound());
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Players;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
@@ -58,6 +59,9 @@ public sealed partial class TestPair
|
||||
(Server, ServerLogHandler) = await PoolManager.GenerateServer(settings, testOut);
|
||||
ActivateContext(testOut);
|
||||
|
||||
Client.CfgMan.OnCVarValueChanged += OnClientCvarChanged;
|
||||
Server.CfgMan.OnCVarValueChanged += OnServerCvarChanged;
|
||||
|
||||
if (!settings.NoLoadTestPrototypes)
|
||||
await LoadPrototypes(testPrototypes!);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ public static partial class PoolManager
|
||||
(CCVars.NPCMaxUpdates.Name, "999999"),
|
||||
(CVars.ThreadParallelCount.Name, "1"),
|
||||
(CCVars.GameRoleTimers.Name, "false"),
|
||||
(CCVars.GameRoleWhitelist.Name, "false"),
|
||||
(CCVars.GridFill.Name, "false"),
|
||||
(CCVars.PreloadGrids.Name, "false"),
|
||||
(CCVars.ArrivalsShuttles.Name, "false"),
|
||||
|
||||
@@ -270,6 +270,8 @@ public static partial class PoolManager
|
||||
$"{nameof(GetServerClientPair)}: Retrieving pair {pair.Id} from pool took {poolRetrieveTime.TotalMilliseconds} ms");
|
||||
await testOut.WriteLineAsync(
|
||||
$"{nameof(GetServerClientPair)}: Returning pair {pair.Id}");
|
||||
|
||||
pair.ClearModifiedCvars();
|
||||
pair.Settings = poolSettings;
|
||||
pair.TestHistory.Add(currentTestName);
|
||||
pair.Watch.Restart();
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
components:
|
||||
- type: Buckle
|
||||
- type: Hands
|
||||
- type: ComplexInteraction
|
||||
- type: InputMover
|
||||
- type: Body
|
||||
prototype: Human
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace Content.IntegrationTests.Tests
|
||||
[TestOf(typeof(EntityUid))]
|
||||
public sealed class EntityTest
|
||||
{
|
||||
private static readonly ProtoId<EntityCategoryPrototype> SpawnerCategory = "Spawner";
|
||||
|
||||
[Test]
|
||||
public async Task SpawnAndDeleteAllEntitiesOnDifferentMaps()
|
||||
{
|
||||
@@ -234,14 +236,6 @@ namespace Content.IntegrationTests.Tests
|
||||
"StationEvent",
|
||||
"TimedDespawn",
|
||||
|
||||
// Spawner entities
|
||||
"DragonRift",
|
||||
"RandomHumanoidSpawner",
|
||||
"RandomSpawner",
|
||||
"ConditionalSpawner",
|
||||
"GhostRoleMobSpawner",
|
||||
"NukeOperativeSpawner",
|
||||
"TimedSpawner",
|
||||
// makes an announcement on mapInit.
|
||||
"AnnounceOnSpawn",
|
||||
};
|
||||
@@ -253,6 +247,7 @@ namespace Content.IntegrationTests.Tests
|
||||
.Where(p => !p.Abstract)
|
||||
.Where(p => !pair.IsTestPrototype(p))
|
||||
.Where(p => !excluded.Any(p.Components.ContainsKey))
|
||||
.Where(p => p.Categories.All(x => x.ID != SpawnerCategory))
|
||||
.Select(p => p.ID)
|
||||
.ToList();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
||||
components:
|
||||
- type: Cuffable
|
||||
- type: Hands
|
||||
- type: ComplexInteraction
|
||||
- type: Body
|
||||
prototype: Human
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ public sealed class NukeOpsTest
|
||||
var invSys = server.System<InventorySystem>();
|
||||
var factionSys = server.System<NpcFactionSystem>();
|
||||
|
||||
Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False);
|
||||
server.CfgMan.SetCVar(CCVars.GridFill, true);
|
||||
|
||||
// Initially in the lobby
|
||||
@@ -200,7 +199,6 @@ public sealed class NukeOpsTest
|
||||
}
|
||||
|
||||
ticker.SetGamePreset((GamePresetPrototype?)null);
|
||||
server.CfgMan.SetCVar(CCVars.GridFill, false);
|
||||
await pair.SetAntagPref("NukeopsCommander", false);
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Interaction;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -64,6 +65,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
|
||||
{
|
||||
user = sEntities.SpawnEntity(null, coords);
|
||||
sEntities.EnsureComponent<HandsComponent>(user);
|
||||
sEntities.EnsureComponent<ComplexInteractionComponent>(user);
|
||||
handSys.AddHand(user, "hand", HandLocation.Left);
|
||||
target = sEntities.SpawnEntity(null, coords);
|
||||
item = sEntities.SpawnEntity(null, coords);
|
||||
@@ -205,6 +207,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
|
||||
{
|
||||
user = sEntities.SpawnEntity(null, coords);
|
||||
sEntities.EnsureComponent<HandsComponent>(user);
|
||||
sEntities.EnsureComponent<ComplexInteractionComponent>(user);
|
||||
handSys.AddHand(user, "hand", HandLocation.Left);
|
||||
target = sEntities.SpawnEntity(null, new MapCoordinates(new Vector2(SharedInteractionSystem.InteractionRange - 0.1f, 0), mapId));
|
||||
item = sEntities.SpawnEntity(null, coords);
|
||||
@@ -347,6 +350,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click
|
||||
{
|
||||
user = sEntities.SpawnEntity(null, coords);
|
||||
sEntities.EnsureComponent<HandsComponent>(user);
|
||||
sEntities.EnsureComponent<ComplexInteractionComponent>(user);
|
||||
handSys.AddHand(user, "hand", HandLocation.Left);
|
||||
target = sEntities.SpawnEntity(null, coords);
|
||||
item = sEntities.SpawnEntity(null, coords);
|
||||
|
||||
@@ -137,6 +137,7 @@ public abstract partial class InteractionTest
|
||||
prototype: Aghost
|
||||
- type: DoAfter
|
||||
- type: Hands
|
||||
- type: ComplexInteraction
|
||||
- type: MindContainer
|
||||
- type: Stripping
|
||||
- type: Tag
|
||||
|
||||
@@ -103,7 +103,7 @@ public sealed class MaterialArbitrageTest
|
||||
continue;
|
||||
|
||||
var stackProto = protoManager.Index<StackPrototype>(materialStep.MaterialPrototypeId);
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(stackProto.Spawn);
|
||||
var spawnProto = protoManager.Index(stackProto.Spawn);
|
||||
|
||||
if (!spawnProto.Components.ContainsKey(materialName) ||
|
||||
!spawnProto.Components.TryGetValue(compositionName, out var compositionReg))
|
||||
|
||||
@@ -53,11 +53,13 @@ namespace Content.IntegrationTests.Tests.Tag
|
||||
|
||||
EntityUid sTagDummy = default;
|
||||
TagComponent sTagComponent = null!;
|
||||
Entity<TagComponent> sTagEntity = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
sTagDummy = sEntityManager.SpawnEntity(TagEntityId, MapCoordinates.Nullspace);
|
||||
sTagComponent = sEntityManager.GetComponent<TagComponent>(sTagDummy);
|
||||
sTagEntity = new Entity<TagComponent>(sTagDummy, sTagComponent);
|
||||
});
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
@@ -130,49 +132,64 @@ namespace Content.IntegrationTests.Tests.Tag
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Cannot add the starting tag again
|
||||
Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, StartingTag), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, StartingTag), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List<string> { StartingTag, StartingTag }), Is.False);
|
||||
Assert.That(tagSystem.AddTag(sTagEntity, StartingTag), Is.False);
|
||||
|
||||
Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, StartingTag), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagEntity, new List<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.False);
|
||||
|
||||
// Has the starting tag
|
||||
Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True);
|
||||
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { StartingTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
|
||||
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<string> { StartingTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, StartingTag }), Is.True);
|
||||
|
||||
// Does not have the added tag yet
|
||||
Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.False);
|
||||
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { AddedTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
|
||||
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<string> { AddedTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
|
||||
|
||||
// Has a combination of the two tags
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<string> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
|
||||
|
||||
// Does not have both tags
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { StartingTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
|
||||
|
||||
// Cannot remove a tag that does not exist
|
||||
Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List<string> { AddedTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.RemoveTag(sTagEntity, AddedTag), Is.False);
|
||||
|
||||
Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.RemoveTags(sTagEntity, new List<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.RemoveTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { AddedTag, AddedTag }), Is.False);
|
||||
});
|
||||
|
||||
// Can add the new tag
|
||||
Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.True);
|
||||
Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.True);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Cannot add it twice
|
||||
Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.False);
|
||||
|
||||
// Cannot add existing tags
|
||||
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List<string> { StartingTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, AddedTag), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagEntity, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.AddTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
|
||||
|
||||
// Now has two tags
|
||||
Assert.That(sTagComponent.Tags, Has.Count.EqualTo(2));
|
||||
@@ -180,65 +197,103 @@ namespace Content.IntegrationTests.Tests.Tag
|
||||
// Has both tags
|
||||
Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.True);
|
||||
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<string> { AddedTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
|
||||
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.True);
|
||||
Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { AddedTag, StartingTag }), Is.True);
|
||||
});
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// Remove the existing starting tag
|
||||
Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, StartingTag), Is.True);
|
||||
Assert.That(tagSystem.RemoveTag(sTagEntity, StartingTag), Is.True);
|
||||
|
||||
// Remove the existing added tag
|
||||
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.True);
|
||||
Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.True);
|
||||
});
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
// No tags left to remove
|
||||
Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List<string> { StartingTag, AddedTag }), Is.False);
|
||||
Assert.That(tagSystem.RemoveTags(sTagEntity, new List<ProtoId<TagPrototype>> { StartingTag, AddedTag }), Is.False);
|
||||
|
||||
// No tags left in the component
|
||||
Assert.That(sTagComponent.Tags, Is.Empty);
|
||||
});
|
||||
|
||||
#if !DEBUG
|
||||
return;
|
||||
// It is run only in DEBUG build,
|
||||
// as the checks are performed only in DEBUG build.
|
||||
#if DEBUG
|
||||
// Has single
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasTag(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasTag(sTagComponent, UnregisteredTag); });
|
||||
|
||||
// HasAny entityUid methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// HasAny component methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAnyTag(sTagComponent, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// HasAll entityUid methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// HasAll component methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.HasAllTags(sTagComponent, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// RemoveTag single
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTag(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTag(sTagEntity, UnregisteredTag); });
|
||||
|
||||
// RemoveTags entityUid methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// RemoveTags entity methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.RemoveTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// AddTag single
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTag(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTag(sTagEntity, UnregisteredTag); });
|
||||
|
||||
// AddTags entityUid methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagDummy, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
|
||||
// AddTags entity methods
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag, UnregisteredTag); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, new List<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
Assert.Throws<DebugAssertException>(() => { tagSystem.AddTags(sTagEntity, new HashSet<ProtoId<TagPrototype>> { UnregisteredTag }); });
|
||||
#endif
|
||||
|
||||
// Single
|
||||
Assert.Throws<DebugAssertException>(() =>
|
||||
{
|
||||
tagSystem.HasTag(sTagDummy, UnregisteredTag);
|
||||
});
|
||||
Assert.Throws<DebugAssertException>(() =>
|
||||
{
|
||||
tagSystem.HasTag(sTagComponent, UnregisteredTag);
|
||||
});
|
||||
|
||||
// Any
|
||||
Assert.Throws<DebugAssertException>(() =>
|
||||
{
|
||||
tagSystem.HasAnyTag(sTagDummy, UnregisteredTag);
|
||||
});
|
||||
Assert.Throws<DebugAssertException>(() =>
|
||||
{
|
||||
tagSystem.HasAnyTag(sTagComponent, UnregisteredTag);
|
||||
});
|
||||
|
||||
// All
|
||||
Assert.Throws<DebugAssertException>(() =>
|
||||
{
|
||||
tagSystem.HasAllTags(sTagDummy, UnregisteredTag);
|
||||
});
|
||||
Assert.Throws<DebugAssertException>(() =>
|
||||
{
|
||||
tagSystem.HasAllTags(sTagComponent, UnregisteredTag);
|
||||
});
|
||||
});
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Content.IntegrationTests.Tests
|
||||
id: HumanVendingDummy
|
||||
components:
|
||||
- type: Hands
|
||||
- type: ComplexInteraction
|
||||
- type: Body
|
||||
prototype: Human
|
||||
|
||||
|
||||
1913
Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.Designer.cs
generated
Normal file
1913
Content.Server.Database/Migrations/Postgres/20240531011555_RoleWhitelist.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RoleWhitelist : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "role_whitelists",
|
||||
columns: table => new
|
||||
{
|
||||
player_user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
role_id = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_role_whitelists", x => new { x.player_user_id, x.role_id });
|
||||
table.ForeignKey(
|
||||
name: "FK_role_whitelists_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "role_whitelists");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -900,6 +900,22 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.ToTable("profile_role_loadout", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b =>
|
||||
{
|
||||
b.Property<Guid>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("role_id");
|
||||
|
||||
b.HasKey("PlayerUserId", "RoleId")
|
||||
.HasName("PK_role_whitelists");
|
||||
|
||||
b.ToTable("role_whitelists", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1623,6 +1639,19 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("JobWhitelists")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_role_whitelists_player_player_user_id");
|
||||
|
||||
b.Navigation("Player");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
@@ -1822,6 +1851,8 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("AdminWatchlistsLastEdited");
|
||||
|
||||
b.Navigation("AdminWatchlistsReceived");
|
||||
|
||||
b.Navigation("JobWhitelists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
||||
|
||||
1838
Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.Designer.cs
generated
Normal file
1838
Content.Server.Database/Migrations/Sqlite/20240531011549_RoleWhitelist.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RoleWhitelist : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "role_whitelists",
|
||||
columns: table => new
|
||||
{
|
||||
player_user_id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
role_id = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_role_whitelists", x => new { x.player_user_id, x.role_id });
|
||||
table.ForeignKey(
|
||||
name: "FK_role_whitelists_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "role_whitelists");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -847,6 +847,22 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.ToTable("profile_role_loadout", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b =>
|
||||
{
|
||||
b.Property<Guid>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("role_id");
|
||||
|
||||
b.HasKey("PlayerUserId", "RoleId")
|
||||
.HasName("PK_role_whitelists");
|
||||
|
||||
b.ToTable("role_whitelists", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1548,6 +1564,19 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("JobWhitelists")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_role_whitelists_player_player_user_id");
|
||||
|
||||
b.Navigation("Player");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
@@ -1747,6 +1776,8 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("AdminWatchlistsLastEdited");
|
||||
|
||||
b.Navigation("AdminWatchlistsReceived");
|
||||
|
||||
b.Navigation("JobWhitelists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Preference", b =>
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Content.Server.Database
|
||||
public DbSet<AdminNote> AdminNotes { get; set; } = null!;
|
||||
public DbSet<AdminWatchlist> AdminWatchlists { get; set; } = null!;
|
||||
public DbSet<AdminMessage> AdminMessages { get; set; } = null!;
|
||||
public DbSet<RoleWhitelist> RoleWhitelists { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -314,6 +315,13 @@ namespace Content.Server.Database
|
||||
.HasForeignKey(ban => ban.LastEditedById)
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<RoleWhitelist>()
|
||||
.HasOne(w => w.Player)
|
||||
.WithMany(p => p.JobWhitelists)
|
||||
.HasForeignKey(w => w.PlayerUserId)
|
||||
.HasPrincipalKey(p => p.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
|
||||
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
@@ -530,6 +538,7 @@ namespace Content.Server.Database
|
||||
public List<ServerBan> AdminServerBansLastEdited { get; set; } = null!;
|
||||
public List<ServerRoleBan> AdminServerRoleBansCreated { get; set; } = null!;
|
||||
public List<ServerRoleBan> AdminServerRoleBansLastEdited { get; set; } = null!;
|
||||
public List<RoleWhitelist> JobWhitelists { get; set; } = null!;
|
||||
}
|
||||
|
||||
[Table("whitelist")]
|
||||
@@ -1099,4 +1108,15 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
public bool Dismissed { get; set; }
|
||||
}
|
||||
|
||||
[PrimaryKey(nameof(PlayerUserId), nameof(RoleId))]
|
||||
public class RoleWhitelist
|
||||
{
|
||||
[Required, ForeignKey("Player")]
|
||||
public Guid PlayerUserId { get; set; }
|
||||
public Player Player { get; set; } = default!;
|
||||
|
||||
[Required]
|
||||
public string RoleId { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
||||
|
||||
private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
if (args.Handled || !args.Complex)
|
||||
return;
|
||||
|
||||
if (component.ActionEntities is not {} actionEnts)
|
||||
|
||||
@@ -65,13 +65,23 @@ public sealed class BanListEui : BaseEui
|
||||
unban = new SharedServerUnban(unbanningAdmin, ban.Unban.UnbanTime.UtcDateTime);
|
||||
}
|
||||
|
||||
(string, int cidrMask)? ip = ("*Hidden*", 0);
|
||||
var hwid = "*Hidden*";
|
||||
|
||||
if (_admins.HasAdminFlag(Player, AdminFlags.Pii))
|
||||
{
|
||||
ip = ban.Address is { } address
|
||||
? (address.address.ToString(), address.cidrMask)
|
||||
: null;
|
||||
|
||||
hwid = ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan());
|
||||
}
|
||||
|
||||
Bans.Add(new SharedServerBan(
|
||||
ban.Id,
|
||||
ban.UserId,
|
||||
ban.Address is { } address
|
||||
? (address.address.ToString(), address.cidrMask)
|
||||
: null,
|
||||
ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan()),
|
||||
ip,
|
||||
hwid,
|
||||
ban.BanTime.UtcDateTime,
|
||||
ban.ExpirationTime?.UtcDateTime,
|
||||
ban.Reason,
|
||||
@@ -96,13 +106,22 @@ public sealed class BanListEui : BaseEui
|
||||
unban = new SharedServerUnban(unbanningAdmin, ban.Unban.UnbanTime.UtcDateTime);
|
||||
}
|
||||
|
||||
(string, int cidrMask)? ip = ("*Hidden*", 0);
|
||||
var hwid = "*Hidden*";
|
||||
|
||||
if (_admins.HasAdminFlag(Player, AdminFlags.Pii))
|
||||
{
|
||||
ip = ban.Address is { } address
|
||||
? (address.address.ToString(), address.cidrMask)
|
||||
: null;
|
||||
|
||||
hwid = ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan());
|
||||
}
|
||||
RoleBans.Add(new SharedServerRoleBan(
|
||||
ban.Id,
|
||||
ban.UserId,
|
||||
ban.Address is { } address
|
||||
? (address.address.ToString(), address.cidrMask)
|
||||
: null,
|
||||
ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan()),
|
||||
ip,
|
||||
hwid,
|
||||
ban.BanTime.UtcDateTime,
|
||||
ban.ExpirationTime?.UtcDateTime,
|
||||
ban.Reason,
|
||||
|
||||
@@ -7,7 +7,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.AdminWho)]
|
||||
public sealed class AdminWhoCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "adminwho";
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
public sealed class AnnounceUiCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "announceui";
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class BanExemptionUpdateCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
||||
@@ -61,7 +61,7 @@ public sealed class BanExemptionUpdateCommand : LocalizedCommands
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class BanExemptionGetCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _dbManager = default!;
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
sealed class DSay : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Fun)]
|
||||
public sealed class FaxUiCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "faxui";
|
||||
@@ -27,4 +27,3 @@ public sealed class FaxUiCommand : IConsoleCommand
|
||||
eui.OpenEui(ui, player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
214
Content.Server/Administration/Commands/JobWhitelistCommands.cs
Normal file
214
Content.Server/Administration/Commands/JobWhitelistCommands.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.Players.JobWhitelist;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class JobWhitelistAddCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly JobWhitelistManager _jobWhitelist = default!;
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly IPlayerManager _players = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
|
||||
public override string Command => "jobwhitelistadd";
|
||||
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific",
|
||||
("properAmount", 2),
|
||||
("currentAmount", args.Length)));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var player = args[0].Trim();
|
||||
var job = new ProtoId<JobPrototype>(args[1].Trim());
|
||||
if (!_prototypes.TryIndex(job, out var jobPrototype))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-jobwhitelist-job-does-not-exist", ("job", job.Id)));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = await _playerLocator.LookupIdByNameAsync(player);
|
||||
if (data != null)
|
||||
{
|
||||
var guid = data.UserId;
|
||||
var isWhitelisted = await _db.IsJobWhitelisted(guid, job);
|
||||
if (isWhitelisted)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-jobwhitelist-already-whitelisted",
|
||||
("player", player),
|
||||
("jobId", job.Id),
|
||||
("jobName", jobPrototype.LocalizedName)));
|
||||
return;
|
||||
}
|
||||
|
||||
_jobWhitelist.AddWhitelist(guid, job);
|
||||
shell.WriteLine(Loc.GetString("cmd-jobwhitelistadd-added",
|
||||
("player", player),
|
||||
("jobId", job.Id),
|
||||
("jobName", jobPrototype.LocalizedName)));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player)));
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
_players.Sessions.Select(s => s.Name),
|
||||
Loc.GetString("cmd-jobwhitelist-hint-player"));
|
||||
}
|
||||
|
||||
if (args.Length == 2)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
_prototypes.EnumeratePrototypes<JobPrototype>().Select(p => p.ID),
|
||||
Loc.GetString("cmd-jobwhitelist-hint-job"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class GetJobWhitelistCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly IPlayerManager _players = default!;
|
||||
|
||||
public override string Command => "jobwhitelistget";
|
||||
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
shell.WriteError("This command needs at least one argument.");
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var player = string.Join(' ', args).Trim();
|
||||
var data = await _playerLocator.LookupIdByNameAsync(player);
|
||||
if (data != null)
|
||||
{
|
||||
var guid = data.UserId;
|
||||
var whitelists = await _db.GetJobWhitelists(guid);
|
||||
if (whitelists.Count == 0)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-jobwhitelistget-whitelisted-none", ("player", player)));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-jobwhitelistget-whitelisted-for",
|
||||
("player", player),
|
||||
("jobs", string.Join(", ", whitelists))));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player)));
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
_players.Sessions.Select(s => s.Name),
|
||||
Loc.GetString("cmd-jobwhitelist-hint-player"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Ban)]
|
||||
public sealed class RemoveJobWhitelistCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly JobWhitelistManager _jobWhitelist = default!;
|
||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||
[Dependency] private readonly IPlayerManager _players = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
|
||||
public override string Command => "jobwhitelistremove";
|
||||
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific",
|
||||
("properAmount", 2),
|
||||
("currentAmount", args.Length)));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var player = args[0].Trim();
|
||||
var job = new ProtoId<JobPrototype>(args[1].Trim());
|
||||
if (!_prototypes.TryIndex(job, out var jobPrototype))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-jobwhitelist-job-does-not-exist", ("job", job)));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = await _playerLocator.LookupIdByNameAsync(player);
|
||||
if (data != null)
|
||||
{
|
||||
var guid = data.UserId;
|
||||
var isWhitelisted = await _db.IsJobWhitelisted(guid, job);
|
||||
if (!isWhitelisted)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-jobwhitelistremove-was-not-whitelisted",
|
||||
("player", player),
|
||||
("jobId", job.Id),
|
||||
("jobName", jobPrototype.LocalizedName)));
|
||||
return;
|
||||
}
|
||||
|
||||
_jobWhitelist.RemoveWhitelist(guid, job);
|
||||
shell.WriteLine(Loc.GetString("cmd-jobwhitelistremove-removed",
|
||||
("player", player),
|
||||
("jobId", job.Id),
|
||||
("jobName", jobPrototype.LocalizedName)));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player)));
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
_players.Sessions.Select(s => s.Name),
|
||||
Loc.GetString("cmd-jobwhitelist-hint-player"));
|
||||
}
|
||||
|
||||
if (args.Length == 2)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
_prototypes.EnumeratePrototypes<JobPrototype>().Select(p => p.ID),
|
||||
Loc.GetString("cmd-jobwhitelist-hint-job"));
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Fun)]
|
||||
public sealed class LinkBluespaceLocker : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
public sealed class PlayTimeAddOverallCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -58,7 +58,7 @@ public sealed class PlayTimeAddOverallCommand : IConsoleCommand
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
public sealed class PlayTimeAddRoleCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -123,7 +123,7 @@ public sealed class PlayTimeAddRoleCommand : IConsoleCommand
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
public sealed class PlayTimeGetOverallCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -168,7 +168,7 @@ public sealed class PlayTimeGetOverallCommand : IConsoleCommand
|
||||
}
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
public sealed class PlayTimeGetRoleCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -247,7 +247,7 @@ public sealed class PlayTimeGetRoleCommand : IConsoleCommand
|
||||
/// <summary>
|
||||
/// Saves the timers for a particular player immediately
|
||||
/// </summary>
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.Moderator)]
|
||||
public sealed class PlayTimeSaveCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
[AdminCommand(AdminFlags.NameColor)]
|
||||
internal sealed class SetAdminOOC : IConsoleCommand
|
||||
{
|
||||
public string Command => "setadminooc";
|
||||
|
||||
@@ -73,7 +73,9 @@ public sealed class BanManager : IBanManager, IPostInjectInit
|
||||
|
||||
public HashSet<string>? GetRoleBans(NetUserId playerUserId)
|
||||
{
|
||||
return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null;
|
||||
return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans)
|
||||
? roleBans.Select(banDef => banDef.Role).ToHashSet()
|
||||
: null;
|
||||
}
|
||||
|
||||
private async Task CacheDbRoleBans(NetUserId userId, IPAddress? address = null, ImmutableArray<byte>? hwId = null)
|
||||
@@ -263,13 +265,13 @@ public sealed class BanManager : IBanManager, IPostInjectInit
|
||||
return $"Pardoned ban with id {banId}";
|
||||
}
|
||||
|
||||
public HashSet<string>? GetJobBans(NetUserId playerUserId)
|
||||
public HashSet<ProtoId<JobPrototype>>? GetJobBans(NetUserId playerUserId)
|
||||
{
|
||||
if (!_cachedRoleBans.TryGetValue(playerUserId, out var roleBans))
|
||||
return null;
|
||||
return roleBans
|
||||
.Where(ban => ban.Role.StartsWith(JobPrefix, StringComparison.Ordinal))
|
||||
.Select(ban => ban.Role[JobPrefix.Length..])
|
||||
.Select(ban => new ProtoId<JobPrototype>(ban.Role[JobPrefix.Length..]))
|
||||
.ToHashSet();
|
||||
}
|
||||
#endregion
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user