Merge pull request #1331 from NullWanderer/2024/06/06-upstream-merge
Upstream merge
This commit is contained in:
commit
21cd750ff4
|
|
@ -1,19 +1,17 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Warps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
|
|
@ -58,15 +56,20 @@ public class PvsBenchmark
|
|||
_pair.Server.CfgMan.SetCVar(CVars.NetPvsAsync, false);
|
||||
_sys = _entMan.System<SharedTransformSystem>();
|
||||
|
||||
SetupAsync().Wait();
|
||||
}
|
||||
|
||||
private async Task SetupAsync()
|
||||
{
|
||||
// Spawn the map
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
await _pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).Wait();
|
||||
});
|
||||
|
||||
// Get list of ghost warp positions
|
||||
_spawns = _entMan.AllComponentsList<WarpPointComponent>()
|
||||
|
|
@ -76,17 +79,19 @@ public class PvsBenchmark
|
|||
|
||||
Array.Resize(ref _players, PlayerCount);
|
||||
|
||||
// Spawn "Players".
|
||||
_pair.Server.WaitPost(() =>
|
||||
// Spawn "Players"
|
||||
_players = await _pair.Server.AddDummySessions(PlayerCount);
|
||||
await _pair.Server.WaitPost(() =>
|
||||
{
|
||||
var mind = _pair.Server.System<MindSystem>();
|
||||
for (var i = 0; i < PlayerCount; i++)
|
||||
{
|
||||
var pos = _spawns[i % _spawns.Length];
|
||||
var uid =_entMan.SpawnEntity("MobHuman", pos);
|
||||
_pair.Server.ConsoleHost.ExecuteCommand($"setoutfit {_entMan.GetNetEntity(uid)} CaptainGear");
|
||||
_players[i] = new DummySession{AttachedEntity = uid};
|
||||
mind.ControlMob(_players[i].UserId, uid);
|
||||
}
|
||||
}).Wait();
|
||||
});
|
||||
|
||||
// Repeatedly move players around so that they "explore" the map and see lots of entities.
|
||||
// This will populate their PVS data with out-of-view entities.
|
||||
|
|
@ -168,20 +173,4 @@ public class PvsBenchmark
|
|||
}).Wait();
|
||||
_pair.Server.PvsTick(_players);
|
||||
}
|
||||
|
||||
private sealed class DummySession : ICommonSession
|
||||
{
|
||||
public SessionStatus Status => SessionStatus.InGame;
|
||||
public EntityUid? AttachedEntity {get; set; }
|
||||
public NetUserId UserId => default;
|
||||
public string Name => string.Empty;
|
||||
public short Ping => default;
|
||||
public INetChannel Channel { get; set; } = default!;
|
||||
public LoginType AuthType => default;
|
||||
public HashSet<EntityUid> ViewSubscriptions { get; } = new();
|
||||
public DateTime ConnectedTime { get; set; }
|
||||
public SessionState State => default!;
|
||||
public SessionData Data => default!;
|
||||
public bool ClientSide { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
|
|
@ -40,7 +42,7 @@ namespace Content.Client.Access.UI
|
|||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(string newJobIconId)
|
||||
public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
|
|||
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetAllowedIcons(HashSet<string> icons, string currentJobIconId)
|
||||
public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
|
|
@ -46,10 +46,8 @@ namespace Content.Client.Access.UI
|
|||
var i = 0;
|
||||
foreach (var jobIconId in icons)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<StatusIconPrototype>(jobIconId, out var jobIcon))
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
|
||||
continue;
|
||||
}
|
||||
|
||||
String styleBase = StyleBase.ButtonOpenBoth;
|
||||
var modulo = i % JobIconColumnCount;
|
||||
|
|
@ -77,7 +75,7 @@ namespace Content.Client.Access.UI
|
|||
};
|
||||
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
if (jobIconId.Equals(currentJobIconId))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,15 +1,21 @@
|
|||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:ot="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
|
||||
Text="{Loc Object type:}" />
|
||||
<LineEdit Name="SearchLineEdit" PlaceHolder="{Loc Search...}" HorizontalExpand="True" SizeFlagsStretchRatio="1"/>
|
||||
<OptionButton Name="ObjectTypeOptions" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" Name="ObjectList">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<ot:ObjectsTabHeader Name="ListHeader"/>
|
||||
<cc:HSeparator/>
|
||||
<co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using Content.Client.Station;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
|
@ -10,20 +12,20 @@ namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTab : Control
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly List<ObjectsTabEntry> _objects = new();
|
||||
private List<ObjectsTabSelection> _selections = new();
|
||||
private readonly List<ObjectsTabSelection> _selections = new();
|
||||
private bool _ascending = false; // Set to false for descending order by default
|
||||
private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
|
||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||
|
||||
public event Action<ObjectsTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
|
||||
// Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController
|
||||
// OR
|
||||
// I can do this.
|
||||
private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
|
||||
|
||||
private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2);
|
||||
private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
|
||||
private TimeSpan _nextUpdate;
|
||||
|
||||
public ObjectsTab()
|
||||
{
|
||||
|
|
@ -42,6 +44,30 @@ public sealed partial class ObjectsTab : Control
|
|||
ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!);
|
||||
}
|
||||
|
||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||
SearchList.SearchBar = SearchLineEdit;
|
||||
SearchList.GenerateItem += GenerateButton;
|
||||
SearchList.DataFilterCondition += DataFilterCondition;
|
||||
|
||||
RefreshObjectList();
|
||||
// Set initial selection and refresh the list to apply the initial sort order
|
||||
var defaultSelection = ObjectsTabSelection.Grids;
|
||||
ObjectTypeOptions.SelectId((int)defaultSelection); // Set the default selection
|
||||
RefreshObjectList(defaultSelection); // Refresh the list with the default selection
|
||||
|
||||
// Initialize the next update time
|
||||
_nextUpdate = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_timing.CurTime < _nextUpdate)
|
||||
return;
|
||||
|
||||
_nextUpdate = _timing.CurTime + _updateFrequency;
|
||||
|
||||
RefreshObjectList();
|
||||
}
|
||||
|
||||
|
|
@ -81,32 +107,72 @@ public sealed partial class ObjectsTab : Control
|
|||
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
|
||||
}
|
||||
|
||||
foreach (var control in _objects)
|
||||
entities.Sort((a, b) =>
|
||||
{
|
||||
ObjectList.RemoveChild(control);
|
||||
var valueA = GetComparableValue(a, _headerClicked);
|
||||
var valueB = GetComparableValue(b, _headerClicked);
|
||||
return _ascending ? Comparer<object>.Default.Compare(valueA, valueB) : Comparer<object>.Default.Compare(valueB, valueA);
|
||||
});
|
||||
|
||||
var listData = new List<ObjectsListData>();
|
||||
for (int index = 0; index < entities.Count; index++)
|
||||
{
|
||||
var info = entities[index];
|
||||
listData.Add(new ObjectsListData(info, $"{info.Name} {info.Entity}", index % 2 == 0 ? _altColor : _defaultColor));
|
||||
}
|
||||
|
||||
_objects.Clear();
|
||||
|
||||
foreach (var (name, nent) in entities)
|
||||
{
|
||||
var ctrl = new ObjectsTabEntry(name, nent);
|
||||
_objects.Add(ctrl);
|
||||
ObjectList.AddChild(ctrl);
|
||||
ctrl.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(ctrl, args);
|
||||
}
|
||||
SearchList.PopulateList(listData);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_timing.CurTime < _nextUpdate)
|
||||
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
|
||||
return;
|
||||
|
||||
// I do not care for precision.
|
||||
_nextUpdate = _timing.CurTime + _updateFrequency;
|
||||
var entry = new ObjectsTabEntry(info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
|
||||
button.ToolTip = $"{info.Name}, {info.Entity}";
|
||||
|
||||
// Add key binding event handler
|
||||
entry.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
|
||||
button.AddChild(entry);
|
||||
}
|
||||
|
||||
private bool DataFilterCondition(string filter, ListData listData)
|
||||
{
|
||||
if (listData is not ObjectsListData { FilteringString: var filteringString })
|
||||
return false;
|
||||
|
||||
// If the filter is empty, do not filter out any entries
|
||||
if (string.IsNullOrEmpty(filter))
|
||||
return true;
|
||||
|
||||
return filteringString.Contains(filter, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private object GetComparableValue((string Name, NetEntity Entity) entity, ObjectsTabHeader.Header header)
|
||||
{
|
||||
return header switch
|
||||
{
|
||||
ObjectsTabHeader.Header.ObjectName => entity.Name,
|
||||
ObjectsTabHeader.Header.EntityID => entity.Entity.ToString(),
|
||||
_ => entity.Name
|
||||
};
|
||||
}
|
||||
|
||||
private void HeaderClicked(ObjectsTabHeader.Header header)
|
||||
{
|
||||
if (_headerClicked == header)
|
||||
{
|
||||
_ascending = !_ascending;
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerClicked = header;
|
||||
_ascending = true;
|
||||
}
|
||||
|
||||
ListHeader.UpdateHeaderSymbols(_headerClicked, _ascending);
|
||||
RefreshObjectList();
|
||||
}
|
||||
|
||||
|
|
@ -118,3 +184,4 @@ public sealed partial class ObjectsTab : Control
|
|||
}
|
||||
}
|
||||
|
||||
public record ObjectsListData((string Name, NetEntity Entity) Info, string FilteringString, Color BackgroundColor) : 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">
|
||||
|
|
@ -14,4 +14,4 @@
|
|||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</ContainerButton>
|
||||
</PanelContainer>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabEntry : ContainerButton
|
||||
public sealed partial class ObjectsTabEntry : PanelContainer
|
||||
{
|
||||
public NetEntity AssocEntity;
|
||||
|
||||
public ObjectsTabEntry(string name, NetEntity nent)
|
||||
public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
AssocEntity = nent;
|
||||
EIDLabel.Text = nent.ToString();
|
||||
NameLabel.Text = name;
|
||||
BackgroundColorPanel.PanelOverride = styleBox;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer Name="BackgroundColorPanel" Access="Public"/>
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="ObjectNameLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-object-name}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="EntityIDLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-entity-id}"
|
||||
MouseFilter="Pass"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTabHeader : Control
|
||||
{
|
||||
public event Action<Header>? OnHeaderClicked;
|
||||
|
||||
private const string ArrowUp = "↑";
|
||||
private const string ArrowDown = "↓";
|
||||
|
||||
public ObjectsTabHeader()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
ObjectNameLabel.OnKeyBindDown += ObjectNameClicked;
|
||||
EntityIDLabel.OnKeyBindDown += EntityIDClicked;
|
||||
}
|
||||
|
||||
public Label GetHeader(Header header)
|
||||
{
|
||||
return header switch
|
||||
{
|
||||
Header.ObjectName => ObjectNameLabel,
|
||||
Header.EntityID => EntityIDLabel,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
|
||||
};
|
||||
}
|
||||
|
||||
public void ResetHeaderText()
|
||||
{
|
||||
ObjectNameLabel.Text = Loc.GetString("object-tab-object-name");
|
||||
EntityIDLabel.Text = Loc.GetString("object-tab-entity-id");
|
||||
}
|
||||
|
||||
public void UpdateHeaderSymbols(Header headerClicked, bool ascending)
|
||||
{
|
||||
ResetHeaderText();
|
||||
var arrow = ascending ? ArrowUp : ArrowDown;
|
||||
GetHeader(headerClicked).Text += $" {arrow}";
|
||||
}
|
||||
|
||||
private void HeaderClicked(GUIBoundKeyEventArgs args, Header header)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnHeaderClicked?.Invoke(header);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
private void ObjectNameClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.ObjectName);
|
||||
}
|
||||
|
||||
private void EntityIDClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.EntityID);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
ObjectNameLabel.OnKeyBindDown -= ObjectNameClicked;
|
||||
EntityIDLabel.OnKeyBindDown -= EntityIDClicked;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Header
|
||||
{
|
||||
ObjectName,
|
||||
EntityID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
|
||||
}
|
||||
|
||||
#region Antag Overlay
|
||||
|
|
@ -110,7 +111,9 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
|||
_players = players;
|
||||
PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
|
||||
|
||||
var sortedPlayers = new List<PlayerInfo>(players);
|
||||
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
|
||||
|
||||
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
|
||||
sortedPlayers.Sort(Compare);
|
||||
|
||||
UpdateHeaderSymbols();
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Antag;
|
||||
|
||||
/// <summary>
|
||||
/// Used for assigning specified icons for antags.
|
||||
/// </summary>
|
||||
public sealed class AntagStatusIconSystem : SharedStatusIconSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetIcon);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Status Icon on an entity if the player is supposed to see it.
|
||||
/// </summary>
|
||||
private void GetIcon<T>(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where T: IAntagStatusIconComponent
|
||||
{
|
||||
var ent = _player.LocalSession?.AttachedEntity;
|
||||
|
||||
var canEv = new CanDisplayStatusIconsEvent(ent);
|
||||
RaiseLocalEvent(uid, ref canEv);
|
||||
|
||||
if (!canEv.Cancelled)
|
||||
ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal
|
||||
/// with a special case where if someone is a head rev we only want to display the headrev icon.
|
||||
/// </summary>
|
||||
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||
return;
|
||||
|
||||
GetIcon(uid, comp, ref ev);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Shared.Chemistry;
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Rounding;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -150,6 +151,9 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
|||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
if (!TryComp<ItemComponent>(uid, out var item))
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
|
||||
return;
|
||||
|
||||
|
|
@ -159,7 +163,8 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
|||
{
|
||||
var layer = new PrototypeLayerData();
|
||||
|
||||
var key = "inhand-" + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
var heldPrefix = item.HeldPrefix == null ? "inhand-" : $"{item.HeldPrefix}-inhand-";
|
||||
var key = heldPrefix + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
|
||||
|
||||
layer.State = key;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public sealed class ShowHealthBarsCommand : LocalizedCommands
|
|||
var showHealthBarsComponent = new ShowHealthBarsComponent
|
||||
{
|
||||
DamageContainers = args.ToList(),
|
||||
HealthStatusIcon = null,
|
||||
NetSyncEnabled = false
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace Content.Client.Construction
|
|||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
|
|
@ -195,9 +196,8 @@ namespace Content.Client.Construction
|
|||
if (GhostPresent(loc))
|
||||
return false;
|
||||
|
||||
// This InRangeUnobstructed should probably be replaced with "is there something blocking us in that tile?"
|
||||
var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem));
|
||||
if (!_interactionSystem.InRangeUnobstructed(user, loc, 20f, predicate: predicate))
|
||||
if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate))
|
||||
return false;
|
||||
|
||||
if (!CheckConstructionConditions(prototype, loc, dir, user, showPopup: true))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
@ -157,7 +161,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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
|
|
@ -43,7 +44,7 @@ public sealed partial class UltraVisionOverlay : Overlay
|
|||
|
||||
var worldHandle = args.WorldHandle;
|
||||
var viewport = args.WorldBounds;
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_ultraVisionShader);
|
||||
worldHandle.DrawRect(viewport, Color.White);
|
||||
worldHandle.UseShader(null); // important - as of writing, construction overlay breaks without this
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Shipyard;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -10,6 +11,7 @@ public sealed class ShipyardConsoleBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
private readonly AccessReaderSystem _access;
|
||||
|
||||
|
|
@ -25,7 +27,7 @@ public sealed class ShipyardConsoleBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new ShipyardConsoleMenu(Owner, _proto, EntMan, _player, _access);
|
||||
_menu = new ShipyardConsoleMenu(Owner, _proto, EntMan, _player, _access, _whitelistSystem);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
_menu.OnPurchased += Purchase;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Content.Client.UserInterface.Controls;
|
|||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Shipyard;
|
||||
using Content.Shared.Shipyard.Prototypes;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
|
|
@ -25,7 +26,7 @@ public sealed partial class ShipyardConsoleMenu : FancyWindow
|
|||
public Entity<ShipyardConsoleComponent> Console;
|
||||
private string? _category;
|
||||
|
||||
public ShipyardConsoleMenu(EntityUid console, IPrototypeManager proto, IEntityManager entMan, IPlayerManager player, AccessReaderSystem access)
|
||||
public ShipyardConsoleMenu(EntityUid console, IPrototypeManager proto, IEntityManager entMan, IPlayerManager player, AccessReaderSystem access, EntityWhitelistSystem whitelist)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
|
@ -37,7 +38,7 @@ public sealed partial class ShipyardConsoleMenu : FancyWindow
|
|||
// don't include ships that aren't allowed by whitelist, server won't accept them anyway
|
||||
foreach (var vessel in proto.EnumeratePrototypes<VesselPrototype>())
|
||||
{
|
||||
if (vessel.Whitelist?.IsValid(console, entMan) != false)
|
||||
if(whitelist.IsWhitelistPass(vessel.Whitelist, console))
|
||||
_vessels.Add(vessel);
|
||||
}
|
||||
_vessels.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ using Content.Client.Lobby;
|
|||
using Content.Client.RoundEnd;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.GameWindow;
|
||||
using Content.Shared.Roles;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameTicking.Managers
|
||||
{
|
||||
|
|
@ -17,10 +19,9 @@ namespace Content.Client.GameTicking.Managers
|
|||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
private Dictionary<NetEntity, Dictionary<string, uint?>> _jobsAvailable = new();
|
||||
private Dictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> _jobsAvailable = new();
|
||||
private Dictionary<NetEntity, string> _stationNames = new();
|
||||
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
|
|
@ -32,13 +33,13 @@ namespace Content.Client.GameTicking.Managers
|
|||
[ViewVariables] public TimeSpan StartTime { get; private set; }
|
||||
[ViewVariables] public new bool Paused { get; private set; }
|
||||
|
||||
[ViewVariables] public IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>> JobsAvailable => _jobsAvailable;
|
||||
[ViewVariables] public IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> JobsAvailable => _jobsAvailable;
|
||||
[ViewVariables] public IReadOnlyDictionary<NetEntity, string> StationNames => _stationNames;
|
||||
|
||||
public event Action? InfoBlobUpdated;
|
||||
public event Action? LobbyStatusUpdated;
|
||||
public event Action? LobbyLateJoinStatusUpdated;
|
||||
public event Action<IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>>>? LobbyJobsAvailableUpdated;
|
||||
public event Action<IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>>>? LobbyJobsAvailableUpdated;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -69,7 +70,7 @@ namespace Content.Client.GameTicking.Managers
|
|||
// reading the console. E.g., logs like this one could leak the nuke station/grid:
|
||||
// > Grid NT-Arrivals 1101 (122/n25896) changed parent. Old parent: map 10 (121/n25895). New parent: FTL (123/n26470)
|
||||
#if !DEBUG
|
||||
_map.Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning;
|
||||
EntityManager.System<SharedMapSystem>().Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -244,7 +244,7 @@ namespace Content.Client.LateJoin
|
|||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(prototype.Icon);
|
||||
var jobIcon = _prototypeManager.Index(prototype.Icon);
|
||||
icon.Texture = _sprites.Frame0(jobIcon.Icon);
|
||||
jobSelector.AddChild(icon);
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ namespace Content.Client.LateJoin
|
|||
}
|
||||
}
|
||||
|
||||
private void JobsAvailableUpdated(IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>> updatedJobs)
|
||||
private void JobsAvailableUpdated(IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> updatedJobs)
|
||||
{
|
||||
foreach (var stationEntries in updatedJobs)
|
||||
{
|
||||
|
|
@ -337,10 +337,10 @@ namespace Content.Client.LateJoin
|
|||
public Label JobLabel { get; }
|
||||
public string JobId { get; }
|
||||
public string JobLocalisedName { get; }
|
||||
public uint? Amount { get; private set; }
|
||||
public int? Amount { get; private set; }
|
||||
private bool _initialised = false;
|
||||
|
||||
public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? amount)
|
||||
public JobButton(Label jobLabel, ProtoId<JobPrototype> jobId, string jobLocalisedName, int? amount)
|
||||
{
|
||||
JobLabel = jobLabel;
|
||||
JobId = jobId;
|
||||
|
|
@ -350,7 +350,7 @@ namespace Content.Client.LateJoin
|
|||
_initialised = true;
|
||||
}
|
||||
|
||||
public void RefreshLabel(uint? amount)
|
||||
public void RefreshLabel(int? amount)
|
||||
{
|
||||
if (Amount == amount && _initialised)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -207,8 +207,11 @@ namespace Content.Client.Light
|
|||
|
||||
public static Color GetCurrentRgbColor(TimeSpan curTime, TimeSpan offset, Entity<RgbLightControllerComponent> rgb)
|
||||
{
|
||||
var delta = (float)(curTime - offset).TotalSeconds;
|
||||
var entOffset = Math.Abs(rgb.Owner.Id * 0.09817f);
|
||||
var hue = (delta * rgb.Comp.CycleRate + entOffset) % 1;
|
||||
return Color.FromHsv(new Vector4(
|
||||
(float) (((curTime.TotalSeconds - offset.TotalSeconds) * rgb.Comp.CycleRate + Math.Abs(rgb.Owner.Id * 0.1)) % 1),
|
||||
MathF.Abs(hue),
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
|||
{
|
||||
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
return _prototypeManager.Index<JobPrototype>(highPriorityJob ?? SharedGameTicker.FallbackOverflowJob);
|
||||
return _prototypeManager.Index<JobPrototype>(highPriorityJob.Id ?? SharedGameTicker.FallbackOverflowJob);
|
||||
}
|
||||
|
||||
public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ public sealed partial class CharacterPickerButton : ContainerButton
|
|||
.LoadProfileEntity(humanoid, null, true);
|
||||
|
||||
var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
if (highPriorityJob != null)
|
||||
if (highPriorityJob != default)
|
||||
{
|
||||
var jobName = prototypeManager.Index<JobPrototype>(highPriorityJob).LocalizedName;
|
||||
var jobName = prototypeManager.Index(highPriorityJob).LocalizedName;
|
||||
description = $"{description}\n{jobName}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Client.Lobby.UI.Loadouts;
|
|||
using Content.Client.Lobby.UI.Roles;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Clothing;
|
||||
|
|
@ -466,38 +467,96 @@ namespace Content.Client.Lobby.UI
|
|||
var traits = _prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
|
||||
TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
|
||||
|
||||
if (traits.Count > 0)
|
||||
{
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
if (Profile?.TraitPreferences.Contains(trait.ID) == true)
|
||||
{
|
||||
selector.Preference = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
selector.Preference = false;
|
||||
}
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, preference);
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (traits.Count < 1)
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
// TODO: Localise
|
||||
Text = "No traits available :(",
|
||||
Text = Loc.GetString("humanoid-profile-editor-no-traits"),
|
||||
FontColorOverride = Color.Gray,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//Setup model
|
||||
Dictionary<string, List<string>> model = new();
|
||||
List<string> defaultTraits = new();
|
||||
model.Add("default", defaultTraits);
|
||||
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
if (trait.Category == null)
|
||||
{
|
||||
defaultTraits.Add(trait.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!model.ContainsKey(trait.Category))
|
||||
{
|
||||
model.Add(trait.Category, new());
|
||||
}
|
||||
model[trait.Category].Add(trait.ID);
|
||||
}
|
||||
|
||||
//Create UI view from model
|
||||
foreach (var (categoryId, traitId) in model)
|
||||
{
|
||||
TraitCategoryPrototype? category = null;
|
||||
if (categoryId != "default")
|
||||
{
|
||||
category = _prototypeManager.Index<TraitCategoryPrototype>(categoryId);
|
||||
// Label
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString(category.Name),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
StyleClasses = { StyleBase.StyleClassLabelHeading },
|
||||
});
|
||||
}
|
||||
|
||||
List<TraitPreferenceSelector?> selectors = new();
|
||||
var selectionCount = 0;
|
||||
|
||||
foreach (var traitProto in traitId)
|
||||
{
|
||||
var trait = _prototypeManager.Index<TraitPrototype>(traitProto);
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true;
|
||||
if (selector.Preference)
|
||||
selectionCount += trait.Cost;
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, categoryId, preference);
|
||||
SetDirty();
|
||||
RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
|
||||
};
|
||||
selectors.Add(selector);
|
||||
}
|
||||
|
||||
// Selection counter
|
||||
if (category is { MaxTraitPoints: >= 0 })
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
|
||||
FontColorOverride = Color.Gray
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var selector in selectors)
|
||||
{
|
||||
if (selector == null)
|
||||
continue;
|
||||
|
||||
if (category is { MaxTraitPoints: >= 0 } &&
|
||||
selector.Cost + selectionCount > category.MaxTraitPoints)
|
||||
{
|
||||
selector.Checkbox.Label.FontColorOverride = Color.Red;
|
||||
}
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -562,7 +621,8 @@ namespace Content.Client.Lobby.UI
|
|||
selector.Setup(items, title, 250, description);
|
||||
selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);
|
||||
|
||||
if (!_requirements.CheckRoleTime(antag.Requirements, out var reason))
|
||||
var requirements = _entManager.System<SharedRoleSystem>().GetAntagRequirement(antag);
|
||||
if (!_requirements.CheckRoleTime(requirements, out var reason))
|
||||
{
|
||||
selector.LockRequirements(reason);
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, false);
|
||||
|
|
@ -719,8 +779,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[]
|
||||
{
|
||||
|
|
@ -774,7 +843,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();
|
||||
|
||||
|
|
@ -797,7 +866,7 @@ namespace Content.Client.Lobby.UI
|
|||
TextureScale = new Vector2(2, 2),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
var jobIcon = _prototypeManager.Index<StatusIconPrototype>(job.Icon);
|
||||
var jobIcon = _prototypeManager.Index(job.Icon);
|
||||
icon.Texture = jobIcon.Icon.Frame0();
|
||||
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon);
|
||||
|
||||
|
|
@ -821,13 +890,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).
|
||||
|
|
@ -932,6 +1003,11 @@ namespace Content.Client.Lobby.UI
|
|||
SetDirty();
|
||||
ReloadPreview();
|
||||
};
|
||||
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
UpdateJobPriorities();
|
||||
}
|
||||
|
||||
private void OnFlavorTextChange(string content)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<BoxContainer Name="Container"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox Name="Checkbox"/>
|
||||
<CheckBox Name="Checkbox" Access="Public"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace Content.Client.Lobby.UI.Roles;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TraitPreferenceSelector : Control
|
||||
{
|
||||
public int Cost;
|
||||
|
||||
public bool Preference
|
||||
{
|
||||
get => Checkbox.Pressed;
|
||||
|
|
@ -20,7 +22,12 @@ public sealed partial class TraitPreferenceSelector : Control
|
|||
public TraitPreferenceSelector(TraitPrototype trait)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
Checkbox.Text = Loc.GetString(trait.Name);
|
||||
|
||||
var text = trait.Cost != 0 ? $"[{trait.Cost}] " : "";
|
||||
text += Loc.GetString(trait.Name);
|
||||
|
||||
Cost = trait.Cost;
|
||||
Checkbox.Text = text;
|
||||
Checkbox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
if (trait.Description is { } desc)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,94 +2,115 @@
|
|||
using Content.Client.Buckle;
|
||||
using Content.Client.Gravity;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : EntitySystem
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly GravitySystem _gravity = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, MoveInputEvent>(OnMovementInput);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StartedWaddlingEvent>(OnStartedWalking);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StoppedWaddlingEvent>(OnStoppedWalking);
|
||||
base.Initialize();
|
||||
|
||||
SubscribeAllEvent<StartedWaddlingEvent>(OnStartWaddling);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StunnedEvent>(OnStunned);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, KnockedDownEvent>(OnKnockedDown);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, BuckleChangeEvent>(OnBuckleChange);
|
||||
SubscribeAllEvent<StoppedWaddlingEvent>(OnStopWaddling);
|
||||
}
|
||||
|
||||
private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent args)
|
||||
private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.HasDirectionalMovement && component.IsCurrentlyWaddling)
|
||||
{
|
||||
var stopped = new StoppedWaddlingEvent(entity);
|
||||
|
||||
RaiseLocalEvent(entity, ref stopped);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Only start waddling if we're not currently AND we're actually moving.
|
||||
if (component.IsCurrentlyWaddling || !args.HasDirectionalMovement)
|
||||
return;
|
||||
|
||||
var started = new StartedWaddlingEvent(entity);
|
||||
|
||||
RaiseLocalEvent(entity, ref started);
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StartWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, StartedWaddlingEvent args)
|
||||
private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(uid, component.KeyName))
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StopWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void StartWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(uid, out var mover))
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
if (_gravity.IsWeightless(uid))
|
||||
if (_gravity.IsWeightless(entity.Owner))
|
||||
return;
|
||||
|
||||
|
||||
if (!_actionBlocker.CanMove(uid, mover))
|
||||
if (!_actionBlocker.CanMove(entity.Owner, mover))
|
||||
return;
|
||||
|
||||
// Do nothing if buckled in
|
||||
if (_buckle.IsBuckled(uid))
|
||||
if (_buckle.IsBuckled(entity.Owner))
|
||||
return;
|
||||
|
||||
// Do nothing if crit or dead (for obvious reasons)
|
||||
if (_mobState.IsIncapacitated(uid))
|
||||
if (_mobState.IsIncapacitated(entity.Owner))
|
||||
return;
|
||||
|
||||
var tumbleIntensity = component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
var len = mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
component.LastStep = !component.LastStep;
|
||||
component.IsCurrentlyWaddling = true;
|
||||
private static float CalculateTumbleIntensity(WaddleAnimationComponent component)
|
||||
{
|
||||
return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
}
|
||||
|
||||
private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover)
|
||||
{
|
||||
return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<WaddleAnimationComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.KeyName)
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
_animation.Stop(entity.Owner, entity.Comp.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(entity.Owner, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
}
|
||||
|
||||
private void PlayWaddleAnimationUsing(Entity<WaddleAnimationComponent> entity, float len, float tumbleIntensity)
|
||||
{
|
||||
entity.Comp.LastStep = !entity.Comp.LastStep;
|
||||
|
||||
var anim = new Animation()
|
||||
{
|
||||
|
|
@ -116,58 +137,13 @@ public sealed class WaddleAnimationSystem : EntitySystem
|
|||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), 0),
|
||||
new AnimationTrackProperty.KeyFrame(component.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), len/2),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animation.Play(uid, anim, component.KeyName);
|
||||
}
|
||||
|
||||
private void OnStoppedWalking(EntityUid uid, WaddleAnimationComponent component, StoppedWaddlingEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, WaddleAnimationComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
var started = new StartedWaddlingEvent(uid);
|
||||
|
||||
RaiseLocalEvent(uid, ref started);
|
||||
}
|
||||
|
||||
private void OnStunned(EntityUid uid, WaddleAnimationComponent component, StunnedEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnKnockedDown(EntityUid uid, WaddleAnimationComponent component, KnockedDownEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void OnBuckleChange(EntityUid uid, WaddleAnimationComponent component, BuckleChangeEvent args)
|
||||
{
|
||||
StopWaddling(uid, component);
|
||||
}
|
||||
|
||||
private void StopWaddling(EntityUid uid, WaddleAnimationComponent component)
|
||||
{
|
||||
if (!component.IsCurrentlyWaddling)
|
||||
return;
|
||||
|
||||
_animation.Stop(uid, component.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
|
||||
component.IsCurrentlyWaddling = false;
|
||||
_animation.Play(entity.Owner, anim, entity.Comp.KeyName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
|
|
@ -43,7 +44,7 @@ public sealed partial class DogVisionOverlay : Overlay
|
|||
|
||||
var worldHandle = args.WorldHandle;
|
||||
var viewport = args.WorldBounds;
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_dogVisionShader);
|
||||
worldHandle.DrawRect(viewport, Color.White);
|
||||
worldHandle.UseShader(null); // important - as of writing, construction overlay breaks without this
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
private bool _enabled = false;
|
||||
|
||||
|
|
@ -137,7 +138,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||
|
||||
// check the entity whitelist
|
||||
if (valid && Whitelist != null)
|
||||
valid = Whitelist.IsValid(entity);
|
||||
valid = _whitelistSystem.IsWhitelistPass(Whitelist, entity);
|
||||
|
||||
// and check the cancellable event
|
||||
if (valid && ValidationEvent != null)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Client.UserInterface.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
|
@ -19,19 +22,27 @@ namespace Content.Client.Overlays;
|
|||
public sealed class EntityHealthBarOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _prototype;
|
||||
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly MobStateSystem _mobStateSystem;
|
||||
private readonly MobThresholdSystem _mobThresholdSystem;
|
||||
private readonly StatusIconSystem _statusIconSystem;
|
||||
private readonly ProgressColorSystem _progressColor;
|
||||
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
public HashSet<string> DamageContainers = new();
|
||||
public ProtoId<StatusIconPrototype>? StatusIcon;
|
||||
|
||||
public EntityHealthBarOverlay(IEntityManager entManager)
|
||||
public EntityHealthBarOverlay(IEntityManager entManager, IPrototypeManager prototype)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_prototype = prototype;
|
||||
_transform = _entManager.System<SharedTransformSystem>();
|
||||
_mobStateSystem = _entManager.System<MobStateSystem>();
|
||||
_mobThresholdSystem = _entManager.System<MobThresholdSystem>();
|
||||
_statusIconSystem = _entManager.System<StatusIconSystem>();
|
||||
_progressColor = _entManager.System<ProgressColorSystem>();
|
||||
}
|
||||
|
||||
|
|
@ -42,8 +53,9 @@ 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);
|
||||
_prototype.TryIndex(StatusIcon, out var statusIcon);
|
||||
|
||||
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
|
||||
while (query.MoveNext(out var uid,
|
||||
|
|
@ -52,41 +64,33 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||
out var damageableComponent,
|
||||
out var spriteComponent))
|
||||
{
|
||||
if (_entManager.TryGetComponent<MetaDataComponent>(uid, out var metaDataComponent) &&
|
||||
metaDataComponent.Flags.HasFlag(MetaDataFlags.InContainer))
|
||||
{
|
||||
if (statusIcon != null && !_statusIconSystem.IsVisible((uid, _entManager.GetComponent<MetaDataComponent>(uid)), statusIcon))
|
||||
continue;
|
||||
}
|
||||
|
||||
// We want the stealth user to still be able to see his health bar himself
|
||||
if (!xformQuery.TryGetComponent(uid, out var xform) ||
|
||||
xform.MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (damageableComponent.DamageContainerID == null || !DamageContainers.Contains(damageableComponent.DamageContainerID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// we use the status icon component bounds if specified otherwise use sprite
|
||||
var bounds = _entManager.GetComponentOrNull<StatusIconComponent>(uid)?.Bounds ?? spriteComponent.Bounds;
|
||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||
|
||||
if (!bounds.Translated(worldPos).Intersects(args.WorldAABB))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// we are all progressing towards death every day
|
||||
if (CalcProgress(uid, mobStateComponent, damageableComponent, mobThresholdsComponent) is not { } deathProgress)
|
||||
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 +119,7 @@ public sealed class EntityHealthBarOverlay : Overlay
|
|||
handle.DrawRect(pixelDarken, Black.WithAlpha(128));
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3.Identity);
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCrimi
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.StatusIcon.Id, out var iconPrototype))
|
||||
if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ using Content.Shared.Inventory.Events;
|
|||
using Content.Shared.Overlays;
|
||||
using Robust.Client.Graphics;
|
||||
using System.Linq;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
|
|
@ -11,6 +13,7 @@ namespace Content.Client.Overlays;
|
|||
public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComponent>
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
private EntityHealthBarOverlay _overlay = default!;
|
||||
|
||||
|
|
@ -18,16 +21,21 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
|||
{
|
||||
base.Initialize();
|
||||
|
||||
_overlay = new(EntityManager);
|
||||
_overlay = new(EntityManager, _prototype);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
||||
{
|
||||
base.UpdateInternal(component);
|
||||
|
||||
foreach (var damageContainerId in component.Components.SelectMany(x => x.DamageContainers))
|
||||
foreach (var comp in component.Components)
|
||||
{
|
||||
_overlay.DamageContainers.Add(damageContainerId);
|
||||
foreach (var damageContainerId in comp.DamageContainers)
|
||||
{
|
||||
_overlay.DamageContainers.Add(damageContainerId);
|
||||
}
|
||||
|
||||
_overlay.StatusIcon = comp.HealthStatusIcon;
|
||||
}
|
||||
|
||||
if (!_overlayMan.HasOverlay<EntityHealthBarOverlay>())
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
|
||||
|
|
@ -46,7 +45,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
|||
|
||||
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var healthIcons = DecideHealthIcons(entity);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var iconId = JobIconForNoId;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.MindShieldStatusIcon.Id, out var iconPrototype))
|
||||
if (_prototype.TryIndex(component.MindShieldStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,10 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
|
|||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,13 @@ public sealed partial class JobRequirementsManager : ISharedPlaytimeManager
|
|||
if (player == null)
|
||||
return true;
|
||||
|
||||
return CheckRoleTime(job.Requirements, out reason);
|
||||
return CheckRoleTime(job, out reason);
|
||||
}
|
||||
|
||||
public bool CheckRoleTime(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
var reqs = _entManager.System<SharedRoleSystem>().GetJobRequirement(job);
|
||||
return CheckRoleTime(reqs, out reason);
|
||||
}
|
||||
|
||||
public bool CheckRoleTime(HashSet<JobRequirement>? requirements, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
|
|
|
|||
|
|
@ -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,44 +1,37 @@
|
|||
using Content.Shared.Antag;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Revolutionary;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Revolutionary;
|
||||
|
||||
/// <summary>
|
||||
/// Used for the client to get status icons from other revs.
|
||||
/// </summary>
|
||||
public sealed class RevolutionarySystem : EntitySystem
|
||||
public sealed class RevolutionarySystem : SharedRevolutionarySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, CanDisplayStatusIconsEvent>(OnCanShowRevIcon);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether a client should display the rev icon.
|
||||
/// </summary>
|
||||
private void OnCanShowRevIcon<T>(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) where T : IAntagStatusIconComponent
|
||||
private void GetRevIcon(Entity<RevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
args.Cancelled = !CanDisplayIcon(args.User, comp.IconVisibleToGhost);
|
||||
if (HasComp<HeadRevolutionaryComponent>(ent))
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The criteria that determine whether a client should see Rev/Head rev icons.
|
||||
/// </summary>
|
||||
private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost)
|
||||
private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid) || HasComp<RevolutionaryComponent>(uid))
|
||||
return true;
|
||||
|
||||
if (visibleToGhost && HasComp<GhostComponent>(uid))
|
||||
return true;
|
||||
|
||||
return HasComp<ShowRevIconsComponent>(uid);
|
||||
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ public sealed class SSDIndicatorSystem : EntitySystem
|
|||
{
|
||||
if (component.IsSSD &&
|
||||
_cfg.GetCVar(CCVars.ICShowSSDIndicator) &&
|
||||
!args.InContainer &&
|
||||
!_mobState.IsDead(uid) &&
|
||||
!HasComp<ActiveNPCComponent>(uid) &&
|
||||
TryComp<MindContainerComponent>(uid, out var mindContainer) &&
|
||||
mindContainer.ShowExamineInfo)
|
||||
{
|
||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.Icon));
|
||||
args.StatusIcons.Add(_prototype.Index(component.Icon));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,13 +39,13 @@ 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))
|
||||
{
|
||||
if (xform.MapID != args.MapId)
|
||||
if (xform.MapID != args.MapId || !sprite.Visible)
|
||||
continue;
|
||||
|
||||
var bounds = comp.Bounds ?? sprite.Bounds;
|
||||
|
|
@ -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;
|
||||
|
|
@ -72,6 +72,8 @@ public sealed class StatusIconOverlay : Overlay
|
|||
|
||||
foreach (var proto in icons)
|
||||
{
|
||||
if (!_statusIcon.IsVisible((uid, meta), proto))
|
||||
continue;
|
||||
|
||||
var curTime = _timing.RealTime;
|
||||
var texture = _sprite.GetFrame(proto.Icon, curTime);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.StatusIcon;
|
||||
|
|
@ -13,6 +17,8 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
|||
{
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||
|
||||
private bool _globalEnabled;
|
||||
private bool _localEnabled;
|
||||
|
|
@ -54,10 +60,34 @@ public sealed class StatusIconSystem : SharedStatusIconSystem
|
|||
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
return list;
|
||||
|
||||
var inContainer = (meta.Flags & MetaDataFlags.InContainer) != 0;
|
||||
var ev = new GetStatusIconsEvent(list, inContainer);
|
||||
var ev = new GetStatusIconsEvent(list);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
return ev.StatusIcons;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For overlay to check if an entity can be seen.
|
||||
/// </summary>
|
||||
public bool IsVisible(Entity<MetaDataComponent> ent, StatusIconData data)
|
||||
{
|
||||
var viewer = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
// Always show our icons to our entity
|
||||
if (viewer == ent.Owner)
|
||||
return true;
|
||||
|
||||
if (data.VisibleToGhosts && HasComp<GhostComponent>(viewer))
|
||||
return true;
|
||||
|
||||
if (data.HideInContainer && (ent.Comp.Flags & MetaDataFlags.InContainer) != 0)
|
||||
return false;
|
||||
|
||||
if (data.HideOnStealth && TryComp<StealthComponent>(ent, out var stealth) && stealth.Enabled)
|
||||
return false;
|
||||
|
||||
if (data.ShowTo != null && !_entityWhitelist.IsValid(data.ShowTo, viewer))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Client.Interactable.Components;
|
||||
using Content.Client.StatusIcon;
|
||||
using Content.Shared.Stealth;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
|
@ -18,6 +19,7 @@ public sealed class StealthSystem : SharedStealthSystem
|
|||
base.Initialize();
|
||||
|
||||
_shader = _protoMan.Index<ShaderPrototype>("Stealth").InstanceUnique();
|
||||
|
||||
SubscribeLocalEvent<StealthComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<StealthComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender);
|
||||
|
|
@ -93,4 +95,3 @@ public sealed class StealthSystem : SharedStealthSystem
|
|||
args.Sprite.Color = new Color(visibility, visibility, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -289,6 +289,10 @@ public sealed class ActionButton : Control, IEntityControl
|
|||
{
|
||||
if (_action.IconOn != null)
|
||||
SetActionIcon(_spriteSys.Frame0(_action.IconOn));
|
||||
else if (_action.Icon != null)
|
||||
SetActionIcon(_spriteSys.Frame0(_action.Icon));
|
||||
else
|
||||
SetActionIcon(null);
|
||||
|
||||
if (_action.BackgroundOn != null)
|
||||
_buttonBackgroundTexture = _spriteSys.Frame0(_action.BackgroundOn);
|
||||
|
|
|
|||
|
|
@ -198,9 +198,12 @@ public sealed class AdminUIController : UIController,
|
|||
args.Handle();
|
||||
}
|
||||
|
||||
private void ObjectsTabEntryKeyBindDown(ObjectsTabEntry entry, GUIBoundKeyEventArgs args)
|
||||
private void ObjectsTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data)
|
||||
{
|
||||
var uid = entry.AssocEntity;
|
||||
if (data is not ObjectsListData { Info: var info })
|
||||
return;
|
||||
|
||||
var uid = info.Entity;
|
||||
var function = args.Function;
|
||||
|
||||
if (function == EngineKeyFunctions.UIClick)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Shared.Atmos.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
{
|
||||
|
|
@ -30,7 +29,7 @@ namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
|||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = new GasTankWindow(this);
|
||||
_window = new GasTankWindow(this, EntMan.GetComponent<MetaDataComponent>(Owner).EntityName);
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,201 +10,194 @@ using Robust.Client.UserInterface.Controls;
|
|||
using Robust.Client.UserInterface.CustomControls;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank;
|
||||
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
{
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
private readonly RichTextLabel _lblPressure;
|
||||
private readonly FloatSpinBox _spbPressure;
|
||||
private readonly RichTextLabel _lblInternals;
|
||||
private readonly Button _btnInternals;
|
||||
|
||||
public GasTankWindow(GasTankBoundUserInterface owner, string uidName)
|
||||
{
|
||||
private GasTankBoundUserInterface _owner;
|
||||
private readonly Label _lblName;
|
||||
private readonly BoxContainer _topContainer;
|
||||
private readonly Control _contentContainer;
|
||||
Control contentContainer;
|
||||
BoxContainer topContainer;
|
||||
TextureButton btnClose;
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
var rootContainer = new LayoutContainer { Name = "GasTankRoot" };
|
||||
AddChild(rootContainer);
|
||||
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
|
||||
private readonly IResourceCache _resourceCache = default!;
|
||||
private readonly RichTextLabel _lblPressure;
|
||||
private readonly FloatSpinBox _spbPressure;
|
||||
private readonly RichTextLabel _lblInternals;
|
||||
private readonly Button _btnInternals;
|
||||
|
||||
public GasTankWindow(GasTankBoundUserInterface owner)
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
TextureButton btnClose;
|
||||
_resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
_owner = owner;
|
||||
var rootContainer = new LayoutContainer {Name = "GasTankRoot"};
|
||||
AddChild(rootContainer);
|
||||
Texture = panelTex,
|
||||
Modulate = Color.FromHex("#25252A"),
|
||||
};
|
||||
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
var topPanel = new PanelContainer
|
||||
{
|
||||
PanelOverride = back,
|
||||
MouseFilter = MouseFilterMode.Pass
|
||||
};
|
||||
|
||||
var bottomWrap = new LayoutContainer
|
||||
{
|
||||
Name = "BottomWrap"
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topPanel);
|
||||
rootContainer.AddChild(bottomWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetMarginBottom(topPanel, -85);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
|
||||
var topContainerWrap = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
Texture = panelTex,
|
||||
Modulate = Color.FromHex("#25252A"),
|
||||
};
|
||||
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
var topPanel = new PanelContainer
|
||||
{
|
||||
PanelOverride = back,
|
||||
MouseFilter = MouseFilterMode.Pass
|
||||
};
|
||||
|
||||
var bottomWrap = new LayoutContainer
|
||||
{
|
||||
Name = "BottomWrap"
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topPanel);
|
||||
rootContainer.AddChild(bottomWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetMarginBottom(topPanel, -85);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||
|
||||
|
||||
var topContainerWrap = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
(topContainer = new BoxContainer
|
||||
{
|
||||
(_topContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}),
|
||||
new Control {MinSize = new Vector2(0, 110)}
|
||||
}
|
||||
};
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
}),
|
||||
new Control {MinSize = new Vector2(0, 110)}
|
||||
}
|
||||
};
|
||||
|
||||
rootContainer.AddChild(topContainerWrap);
|
||||
rootContainer.AddChild(topContainerWrap);
|
||||
|
||||
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
var font = _resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||
var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||
|
||||
var topRow = new BoxContainer
|
||||
var topRow = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(4, 2, 12, 2),
|
||||
Children =
|
||||
{
|
||||
(new Label
|
||||
{
|
||||
Text = uidName,
|
||||
FontOverride = font,
|
||||
FontColorOverride = StyleNano.NanoGold,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Left,
|
||||
Margin = new Thickness(0, 0, 20, 0),
|
||||
}),
|
||||
(btnClose = new TextureButton
|
||||
{
|
||||
StyleClasses = {DefaultWindow.StyleClassWindowCloseButton},
|
||||
VerticalAlignment = VAlignment.Center
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var middle = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#202025") },
|
||||
Children =
|
||||
{
|
||||
(contentContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8, 4),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
topContainer.AddChild(topRow);
|
||||
topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||
});
|
||||
topContainer.AddChild(middle);
|
||||
topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||
});
|
||||
|
||||
|
||||
_lblPressure = new RichTextLabel();
|
||||
contentContainer.AddChild(_lblPressure);
|
||||
|
||||
//internals
|
||||
_lblInternals = new RichTextLabel
|
||||
{ MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center };
|
||||
_btnInternals = new Button { Text = Loc.GetString("gas-tank-window-internals-toggle-button") };
|
||||
|
||||
contentContainer.AddChild(
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(4, 2, 12, 2),
|
||||
Children =
|
||||
{
|
||||
(_lblName = new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-tank-window-label"),
|
||||
FontOverride = font,
|
||||
FontColorOverride = StyleNano.NanoGold,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Left,
|
||||
Margin = new Thickness(0, 0, 20, 0),
|
||||
}),
|
||||
(btnClose = new TextureButton
|
||||
{
|
||||
StyleClasses = {DefaultWindow.StyleClassWindowCloseButton},
|
||||
VerticalAlignment = VAlignment.Center
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var middle = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#202025")},
|
||||
Children =
|
||||
{
|
||||
(_contentContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Margin = new Thickness(8, 4),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
_topContainer.AddChild(topRow);
|
||||
_topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")}
|
||||
});
|
||||
_topContainer.AddChild(middle);
|
||||
_topContainer.AddChild(new PanelContainer
|
||||
{
|
||||
MinSize = new Vector2(0, 2),
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#525252ff")}
|
||||
Margin = new Thickness(0, 7, 0, 0),
|
||||
Children = { _lblInternals, _btnInternals }
|
||||
});
|
||||
|
||||
|
||||
_lblPressure = new RichTextLabel();
|
||||
_contentContainer.AddChild(_lblPressure);
|
||||
|
||||
//internals
|
||||
_lblInternals = new RichTextLabel
|
||||
{MinSize = new Vector2(200, 0), VerticalAlignment = VAlignment.Center};
|
||||
_btnInternals = new Button {Text = Loc.GetString("gas-tank-window-internals-toggle-button") };
|
||||
|
||||
_contentContainer.AddChild(
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Margin = new Thickness(0, 7, 0, 0),
|
||||
Children = {_lblInternals, _btnInternals}
|
||||
});
|
||||
|
||||
// Separator
|
||||
_contentContainer.AddChild(new Control
|
||||
{
|
||||
MinSize = new Vector2(0, 10)
|
||||
});
|
||||
|
||||
_contentContainer.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-tank-window-output-pressure-label"),
|
||||
Align = Label.AlignMode.Center
|
||||
});
|
||||
_spbPressure = new FloatSpinBox
|
||||
{
|
||||
IsValid = f => f >= 0 || f <= 3000,
|
||||
Margin = new Thickness(25, 0, 25, 7)
|
||||
};
|
||||
_contentContainer.AddChild(_spbPressure);
|
||||
|
||||
// Handlers
|
||||
_spbPressure.OnValueChanged += args =>
|
||||
{
|
||||
_owner.SetOutputPressure(args.Value);
|
||||
};
|
||||
|
||||
_btnInternals.OnPressed += args =>
|
||||
{
|
||||
_owner.ToggleInternals();
|
||||
};
|
||||
|
||||
btnClose.OnPressed += _ => Close();
|
||||
}
|
||||
|
||||
public void UpdateState(GasTankBoundUserInterfaceState state)
|
||||
// Separator
|
||||
contentContainer.AddChild(new Control
|
||||
{
|
||||
_lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}")));
|
||||
_btnInternals.Disabled = !state.CanConnectInternals;
|
||||
_lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text",
|
||||
("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected"))));
|
||||
if (state.OutputPressure.HasValue)
|
||||
{
|
||||
_spbPressure.Value = state.OutputPressure.Value;
|
||||
}
|
||||
}
|
||||
MinSize = new Vector2(0, 10)
|
||||
});
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
contentContainer.AddChild(new Label
|
||||
{
|
||||
return DragMode.Move;
|
||||
}
|
||||
Text = Loc.GetString("gas-tank-window-output-pressure-label"),
|
||||
Align = Label.AlignMode.Center
|
||||
});
|
||||
_spbPressure = new FloatSpinBox
|
||||
{
|
||||
IsValid = f => f >= 0 || f <= 3000,
|
||||
Margin = new Thickness(25, 0, 25, 7)
|
||||
};
|
||||
contentContainer.AddChild(_spbPressure);
|
||||
|
||||
protected override bool HasPoint(Vector2 point)
|
||||
// Handlers
|
||||
_spbPressure.OnValueChanged += args =>
|
||||
{
|
||||
return false;
|
||||
owner.SetOutputPressure(args.Value);
|
||||
};
|
||||
|
||||
_btnInternals.OnPressed += args =>
|
||||
{
|
||||
owner.ToggleInternals();
|
||||
};
|
||||
|
||||
btnClose.OnPressed += _ => Close();
|
||||
}
|
||||
|
||||
public void UpdateState(GasTankBoundUserInterfaceState state)
|
||||
{
|
||||
_lblPressure.SetMarkup(Loc.GetString("gas-tank-window-tank-pressure-text", ("tankPressure", $"{state.TankPressure:0.##}")));
|
||||
_btnInternals.Disabled = !state.CanConnectInternals;
|
||||
_lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text",
|
||||
("status", Loc.GetString(state.InternalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected"))));
|
||||
if (state.OutputPressure.HasValue)
|
||||
{
|
||||
_spbPressure.Value = state.OutputPressure.Value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
{
|
||||
return DragMode.Move;
|
||||
}
|
||||
|
||||
protected override bool HasPoint(Vector2 point)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -81,7 +81,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);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,40 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Zombies;
|
||||
|
||||
public sealed class ZombieSystem : EntitySystem
|
||||
public sealed class ZombieSystem : SharedZombieSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ZombieComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ZombieComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, CanDisplayStatusIconsEvent>(OnCanDisplayStatusIcons);
|
||||
SubscribeLocalEvent<ZombieComponent, GetStatusIconsEvent>(GetZombieIcon);
|
||||
SubscribeLocalEvent<InitialInfectedComponent, GetStatusIconsEvent>(GetInitialInfectedIcon);
|
||||
}
|
||||
|
||||
private void GetZombieIcon(Entity<ZombieComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void GetInitialInfectedIcon(Entity<InitialInfectedComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<ZombieComponent>(ent))
|
||||
return;
|
||||
|
||||
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, ZombieComponent component, ComponentStartup args)
|
||||
|
|
@ -31,29 +50,4 @@ public sealed class ZombieSystem : EntitySystem
|
|||
sprite.LayerSetColor(i, component.SkinColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a player should be able to see the StatusIcon for zombies.
|
||||
/// </summary>
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, ZombieComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<ZombieComponent>(args.User) || HasComp<InitialInfectedComponent>(args.User) || HasComp<ShowZombieIconsComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnCanDisplayStatusIcons(EntityUid uid, InitialInfectedComponent component, ref CanDisplayStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<InitialInfectedComponent>(args.User) && !HasComp<ZombieComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.IconVisibleToGhost && HasComp<GhostComponent>(args.User))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Content.Shared.Preferences;
|
|||
using Content.Shared.Roles;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.UnitTesting;
|
||||
|
||||
|
|
@ -133,27 +134,73 @@ public sealed partial class TestPair
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for enabling or disabling a antag role
|
||||
/// Set a user's antag preferences. Modified preferences are automatically reset at the end of the test.
|
||||
/// </summary>
|
||||
public async Task SetAntagPref(ProtoId<AntagPrototype> id, bool value)
|
||||
public async Task SetAntagPreference(ProtoId<AntagPrototype> id, bool value, NetUserId? user = null)
|
||||
{
|
||||
user ??= Client.User!.Value;
|
||||
if (user is not {} userId)
|
||||
return;
|
||||
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
var prefs = prefMan.GetPreferences(userId);
|
||||
|
||||
var prefs = prefMan.GetPreferences(Client.User!.Value);
|
||||
// what even is the point of ICharacterProfile if we always cast it to HumanoidCharacterProfile to make it usable?
|
||||
var profile = (HumanoidCharacterProfile) prefs.SelectedCharacter;
|
||||
// Automatic preference resetting only resets slot 0.
|
||||
Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0));
|
||||
|
||||
Assert.That(profile.AntagPreferences.Contains(id), Is.EqualTo(!value));
|
||||
var profile = (HumanoidCharacterProfile) prefs.Characters[0];
|
||||
var newProfile = profile.WithAntagPreference(id, value);
|
||||
_modifiedProfiles.Add(userId);
|
||||
await Server.WaitPost(() => prefMan.SetProfile(userId, 0, newProfile).Wait());
|
||||
}
|
||||
|
||||
await Server.WaitPost(() =>
|
||||
/// <summary>
|
||||
/// Set a user's job preferences. Modified preferences are automatically reset at the end of the test.
|
||||
/// </summary>
|
||||
public async Task SetJobPriority(ProtoId<JobPrototype> id, JobPriority value, NetUserId? user = null)
|
||||
{
|
||||
user ??= Client.User!.Value;
|
||||
if (user is { } userId)
|
||||
await SetJobPriorities(userId, (id, value));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="SetJobPriority"/>
|
||||
public async Task SetJobPriorities(params (ProtoId<JobPrototype>, JobPriority)[] priorities)
|
||||
=> await SetJobPriorities(Client.User!.Value, priorities);
|
||||
|
||||
/// <inheritdoc cref="SetJobPriority"/>
|
||||
public async Task SetJobPriorities(NetUserId user, params (ProtoId<JobPrototype>, JobPriority)[] priorities)
|
||||
{
|
||||
var highCount = priorities.Count(x => x.Item2 == JobPriority.High);
|
||||
Assert.That(highCount, Is.LessThanOrEqualTo(1), "Cannot have more than one high priority job");
|
||||
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
var prefs = prefMan.GetPreferences(user);
|
||||
var profile = (HumanoidCharacterProfile) prefs.Characters[0];
|
||||
var dictionary = new Dictionary<ProtoId<JobPrototype>, JobPriority>(profile.JobPriorities);
|
||||
|
||||
// Automatic preference resetting only resets slot 0.
|
||||
Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0));
|
||||
|
||||
if (highCount != 0)
|
||||
{
|
||||
prefMan.SetProfile(Client.User.Value, prefs.SelectedCharacterIndex, newProfile).Wait();
|
||||
});
|
||||
foreach (var (key, priority) in dictionary)
|
||||
{
|
||||
if (priority == JobPriority.High)
|
||||
dictionary[key] = JobPriority.Medium;
|
||||
}
|
||||
}
|
||||
|
||||
// And why the fuck does it always create a new preference and profile object instead of just reusing them?
|
||||
var newPrefs = prefMan.GetPreferences(Client.User.Value);
|
||||
var newProf = (HumanoidCharacterProfile) newPrefs.SelectedCharacter;
|
||||
Assert.That(newProf.AntagPreferences.Contains(id), Is.EqualTo(value));
|
||||
foreach (var (job, priority) in priorities)
|
||||
{
|
||||
if (priority == JobPriority.Never)
|
||||
dictionary.Remove(job);
|
||||
else
|
||||
dictionary[job] = priority;
|
||||
}
|
||||
|
||||
var newProfile = profile.WithJobPriorities(dictionary);
|
||||
_modifiedProfiles.Add(user);
|
||||
await Server.WaitPost(() => prefMan.SetProfile(user, 0, newProfile).Wait());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Exceptions;
|
||||
|
|
@ -34,6 +36,11 @@ public sealed partial class TestPair : IAsyncDisposable
|
|||
|
||||
private async Task OnCleanDispose()
|
||||
{
|
||||
await Server.WaitIdleAsync();
|
||||
await Client.WaitIdleAsync();
|
||||
await ResetModifiedPreferences();
|
||||
await Server.RemoveAllDummySessions();
|
||||
|
||||
if (TestMap != null)
|
||||
{
|
||||
await Server.WaitPost(() => Server.EntMan.DeleteEntity(TestMap.MapUid));
|
||||
|
|
@ -79,6 +86,16 @@ public sealed partial class TestPair : IAsyncDisposable
|
|||
await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: PoolManager took {returnTime.TotalMilliseconds} ms to put pair {Id} back into the pool");
|
||||
}
|
||||
|
||||
private async Task ResetModifiedPreferences()
|
||||
{
|
||||
var prefMan = Server.ResolveDependency<IServerPreferencesManager>();
|
||||
foreach (var user in _modifiedProfiles)
|
||||
{
|
||||
await Server.WaitPost(() => prefMan.SetProfile(user, 0, new HumanoidCharacterProfile()).Wait());
|
||||
}
|
||||
_modifiedProfiles.Clear();
|
||||
}
|
||||
|
||||
public async ValueTask CleanReturnAsync()
|
||||
{
|
||||
if (State != PairState.InUse)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public sealed partial class TestPair
|
|||
public readonly List<string> TestHistory = new();
|
||||
public PoolSettings Settings = default!;
|
||||
public TestMapData? TestMap;
|
||||
private List<NetUserId> _modifiedProfiles = new();
|
||||
|
||||
public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!;
|
||||
public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!;
|
||||
|
||||
|
|
@ -37,7 +39,8 @@ public sealed partial class TestPair
|
|||
client = Client;
|
||||
}
|
||||
|
||||
public ICommonSession? Player => Server.PlayerMan.Sessions.FirstOrDefault();
|
||||
public ICommonSession? Player => Server.PlayerMan.SessionsDict.GetValueOrDefault(Client.User!.Value);
|
||||
|
||||
public ContentPlayerData? PlayerData => Player?.Data.ContentData();
|
||||
|
||||
public PoolTestLogHandler ServerLogHandler { get; private set; } = default!;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public static partial class PoolManager
|
|||
(CCVars.EmergencyShuttleEnabled.Name, "false"),
|
||||
(CCVars.ProcgenPreload.Name, "false"),
|
||||
(CCVars.WorldgenEnabled.Name, "false"),
|
||||
(CCVars.GatewayGeneratorEnabled.Name, "false"),
|
||||
(CVars.ReplayClientRecordingEnabled.Name, "false"),
|
||||
(CVars.ReplayServerRecordingEnabled.Name, "false"),
|
||||
(CCVars.GameDummyTicker.Name, "true"),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue