* Fix usages of TryIndex()
Most usages of TryIndex() were using it incorrectly. Checking whether prototype IDs specified in prototypes actually existed before using them. This is not appropriate as it's just hiding bugs that should be getting caught by the YAML linter and other tools. (#39115)
This then resulted in TryIndex() getting modified to log errors (94f98073b0), which is incorrect as it causes false-positive errors in proper uses of the API: external data validation. (#39098)
This commit goes through and checks every call site of TryIndex() to see whether they were correct. Most call sites were replaced with the new Resolve(), which is suitable for these "defensive programming" use cases.
Fixes #39115
Breaking change: while doing this I noticed IdCardComponent and related systems were erroneously using ProtoId<AccessLevelPrototype> for job prototypes. This has been corrected.
* fix tests
---------
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
158 lines
5.2 KiB
C#
158 lines
5.2 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Client.Roles;
|
|
using Content.Client.Stylesheets;
|
|
using Content.Client.UserInterface.Controls;
|
|
using Content.Shared.Implants;
|
|
using Content.Shared.StatusIcon;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Client.Implants.UI;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class ChameleonControllerMenu : FancyWindow
|
|
{
|
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
|
private readonly SpriteSystem _sprite;
|
|
private readonly JobSystem _job;
|
|
|
|
// List of all the job protos that you can select!
|
|
private IEnumerable<ChameleonOutfitPrototype> _outfits;
|
|
|
|
// Lock the UI until this time
|
|
public DateTime? _lockedUntil;
|
|
|
|
private static readonly ProtoId<JobIconPrototype> UnknownIcon = "JobIconUnknown";
|
|
private static readonly LocId UnknownDepartment = "department-Unknown";
|
|
|
|
public event Action<ProtoId<ChameleonOutfitPrototype>>? OnJobSelected;
|
|
|
|
public ChameleonControllerMenu()
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
IoCManager.InjectDependencies(this);
|
|
_sprite = _entityManager.System<SpriteSystem>();
|
|
_job = _entityManager.System<JobSystem>();
|
|
|
|
_outfits = _prototypeManager.EnumeratePrototypes<ChameleonOutfitPrototype>();
|
|
|
|
UpdateGrid();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill the grid with the correct job icons and buttons.
|
|
/// </summary>
|
|
/// <param name="disabled">Set to true to disable all the buttons.</param>
|
|
public void UpdateGrid(bool disabled = false)
|
|
{
|
|
Grid.RemoveAllChildren();
|
|
|
|
// Dictionary to easily put outfits in departments.
|
|
// Department name -> UI element holding that department.
|
|
var departments = new Dictionary<string, BoxContainer>();
|
|
|
|
departments.Add(UnknownDepartment, CreateDepartment(UnknownDepartment));
|
|
|
|
// Go through every outfit and add them to the correct department.
|
|
foreach (var outfit in _outfits)
|
|
{
|
|
_prototypeManager.Resolve(outfit.Job, out var jobProto);
|
|
|
|
var name = outfit.LoadoutName ?? outfit.Name ?? jobProto?.Name ?? "Prototype has no name or job.";
|
|
|
|
var jobIconId = outfit.Icon ?? jobProto?.Icon ?? UnknownIcon;
|
|
var jobIconProto = _prototypeManager.Index(jobIconId);
|
|
|
|
var outfitButton = CreateOutfitButton(disabled, name, jobIconProto, outfit.ID);
|
|
|
|
if (outfit.Job != null && _job.TryGetLowestWeightDepartment(outfit.Job, out var departmentPrototype))
|
|
{
|
|
if (!departments.ContainsKey(departmentPrototype.Name))
|
|
departments.Add(departmentPrototype.Name, CreateDepartment(departmentPrototype.Name));
|
|
|
|
departments[departmentPrototype.Name].AddChild(outfitButton);
|
|
}
|
|
else
|
|
{
|
|
departments[UnknownDepartment].AddChild(outfitButton);
|
|
}
|
|
}
|
|
|
|
// Sort the departments by their weight.
|
|
var departmentList = departments.ToList();
|
|
departmentList.Sort((a, b) => a.Value.ChildCount.CompareTo(b.Value.ChildCount));
|
|
|
|
// Actually add the departments to the window.
|
|
foreach (var department in departmentList)
|
|
{
|
|
Grid.AddChild(department.Value);
|
|
}
|
|
}
|
|
|
|
private BoxContainer CreateDepartment(string name)
|
|
{
|
|
var departmentContainer = new BoxContainer
|
|
{
|
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
|
};
|
|
departmentContainer.AddChild(new Label
|
|
{
|
|
Text = Loc.GetString(name),
|
|
});
|
|
|
|
return departmentContainer;
|
|
}
|
|
|
|
private BoxContainer CreateOutfitButton(bool disabled, string name, JobIconPrototype jobIconProto, ProtoId<ChameleonOutfitPrototype> outfitProto)
|
|
{
|
|
var outfitButton = new BoxContainer();
|
|
|
|
var button = new Button
|
|
{
|
|
HorizontalExpand = true,
|
|
StyleClasses = {StyleBase.ButtonSquare},
|
|
ToolTip = Loc.GetString(name),
|
|
Text = Loc.GetString(name),
|
|
Margin = new Thickness(0, 0, 15, 0),
|
|
Disabled = disabled,
|
|
};
|
|
|
|
var jobIconTexture = new TextureRect
|
|
{
|
|
Texture = _sprite.Frame0(jobIconProto.Icon),
|
|
TextureScale = new Vector2(2.5f, 2.5f),
|
|
Stretch = TextureRect.StretchMode.KeepCentered,
|
|
Margin = new Thickness(0, 0, 5, 0),
|
|
};
|
|
|
|
outfitButton.AddChild(jobIconTexture);
|
|
outfitButton.AddChild(button);
|
|
|
|
button.OnPressed += _ => JobButtonPressed(outfitProto);
|
|
|
|
return outfitButton;
|
|
}
|
|
|
|
private void JobButtonPressed(ProtoId<ChameleonOutfitPrototype> outfit)
|
|
{
|
|
OnJobSelected?.Invoke(outfit);
|
|
}
|
|
|
|
protected override void FrameUpdate(FrameEventArgs args)
|
|
{
|
|
base.FrameUpdate(args);
|
|
|
|
if (_lockedUntil == null || DateTime.Now < _lockedUntil)
|
|
return;
|
|
|
|
_lockedUntil = null;
|
|
UpdateGrid();
|
|
}
|
|
}
|