* Use new Subs.CVar helper Removes manual config OnValueChanged calls, removes need to remember to manually unsubscribe. This both reduces boilerplate and fixes many issues where subscriptions weren't removed on entity system shutdown. * Fix a bunch of warnings * More warning fixes * Use new DateTime serializer to get rid of ISerializationHooks in changelog code. * Get rid of some more ISerializationHooks for enums * And a little more * Apply suggestions from code review Co-authored-by: 0x6273 <0x40@keemail.me> --------- Co-authored-by: 0x6273 <0x40@keemail.me>
183 lines
7.0 KiB
C#
183 lines
7.0 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Client.UserInterface.Controls;
|
|
using Content.Shared.Access.Components;
|
|
using Content.Shared.Access.Systems;
|
|
using Content.Shared.Research.Components;
|
|
using Content.Shared.Research.Prototypes;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Player;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Client.Research.UI;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class ResearchConsoleMenu : FancyWindow
|
|
{
|
|
public Action<string>? OnTechnologyCardPressed;
|
|
public Action? OnServerButtonPressed;
|
|
|
|
[Dependency] private readonly IEntityManager _entity = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
[Dependency] private readonly IPlayerManager _player = default!;
|
|
private readonly TechnologyDatabaseComponent? _technologyDatabase;
|
|
private readonly ResearchSystem _research;
|
|
private readonly SpriteSystem _sprite;
|
|
private readonly AccessReaderSystem _accessReader;
|
|
|
|
public readonly EntityUid Entity;
|
|
|
|
public ResearchConsoleMenu(EntityUid entity)
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
IoCManager.InjectDependencies(this);
|
|
|
|
_research = _entity.System<ResearchSystem>();
|
|
_sprite = _entity.System<SpriteSystem>();
|
|
_accessReader = _entity.System<AccessReaderSystem>();
|
|
Entity = entity;
|
|
|
|
ServerButton.OnPressed += _ => OnServerButtonPressed?.Invoke();
|
|
|
|
_entity.TryGetComponent(entity, out _technologyDatabase);
|
|
}
|
|
|
|
public void UpdatePanels(ResearchConsoleBoundInterfaceState state)
|
|
{
|
|
TechnologyCardsContainer.Children.Clear();
|
|
|
|
var availableTech = _research.GetAvailableTechnologies(Entity);
|
|
SyncTechnologyList(AvailableCardsContainer, availableTech);
|
|
|
|
if (_technologyDatabase == null)
|
|
return;
|
|
|
|
// i can't figure out the spacing so here you go
|
|
TechnologyCardsContainer.AddChild(new Control
|
|
{
|
|
MinHeight = 10
|
|
});
|
|
|
|
var hasAccess = _player.LocalEntity is not { } local ||
|
|
!_entity.TryGetComponent<AccessReaderComponent>(Entity, out var access) ||
|
|
_accessReader.IsAllowed(local, Entity, access);
|
|
foreach (var techId in _technologyDatabase.CurrentTechnologyCards)
|
|
{
|
|
var tech = _prototype.Index<TechnologyPrototype>(techId);
|
|
var cardControl = new TechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech, includeTier: false), state.Points, hasAccess);
|
|
cardControl.OnPressed += () => OnTechnologyCardPressed?.Invoke(techId);
|
|
TechnologyCardsContainer.AddChild(cardControl);
|
|
}
|
|
|
|
var unlockedTech = _technologyDatabase.UnlockedTechnologies.Select(x => _prototype.Index<TechnologyPrototype>(x));
|
|
SyncTechnologyList(UnlockedCardsContainer, unlockedTech);
|
|
}
|
|
|
|
public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state)
|
|
{
|
|
var amountMsg = new FormattedMessage();
|
|
amountMsg.AddMarkup(Loc.GetString("research-console-menu-research-points-text",
|
|
("points", state.Points)));
|
|
ResearchAmountLabel.SetMessage(amountMsg);
|
|
|
|
if (_technologyDatabase == null)
|
|
return;
|
|
|
|
var disciplineText = Loc.GetString("research-discipline-none");
|
|
var disciplineColor = Color.Gray;
|
|
if (_technologyDatabase.MainDiscipline != null)
|
|
{
|
|
var discipline = _prototype.Index<TechDisciplinePrototype>(_technologyDatabase.MainDiscipline);
|
|
disciplineText = Loc.GetString(discipline.Name);
|
|
disciplineColor = discipline.Color;
|
|
}
|
|
|
|
var msg = new FormattedMessage();
|
|
msg.AddMarkup(Loc.GetString("research-console-menu-main-discipline",
|
|
("name", disciplineText), ("color", disciplineColor)));
|
|
MainDisciplineLabel.SetMessage(msg);
|
|
|
|
TierDisplayContainer.Children.Clear();
|
|
foreach (var disciplineId in _technologyDatabase.SupportedDisciplines)
|
|
{
|
|
var discipline = _prototype.Index<TechDisciplinePrototype>(disciplineId);
|
|
var tier = _research.GetHighestDisciplineTier(_technologyDatabase, discipline);
|
|
|
|
// don't show tiers with no available tech
|
|
if (tier == 0)
|
|
continue;
|
|
|
|
// i'm building the small-ass control here to spare me some mild annoyance in making a new file
|
|
var texture = new TextureRect
|
|
{
|
|
TextureScale = new Vector2( 2, 2 ),
|
|
VerticalAlignment = VAlignment.Center
|
|
};
|
|
var label = new RichTextLabel();
|
|
texture.Texture = _sprite.Frame0(discipline.Icon);
|
|
label.SetMessage(Loc.GetString("research-console-tier-info-small", ("tier", tier)));
|
|
|
|
var control = new BoxContainer
|
|
{
|
|
Children =
|
|
{
|
|
texture,
|
|
label,
|
|
new Control
|
|
{
|
|
MinWidth = 10
|
|
}
|
|
}
|
|
};
|
|
TierDisplayContainer.AddChild(control);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Synchronize a container for technology cards with a list of technologies,
|
|
/// creating or removing UI cards as appropriate.
|
|
/// </summary>
|
|
/// <param name="container">The container which contains the UI cards</param>
|
|
/// <param name="technologies">The current set of technologies for which there should be cards</param>
|
|
private void SyncTechnologyList(BoxContainer container, IEnumerable<TechnologyPrototype> technologies)
|
|
{
|
|
// For the cards which already exist, build a map from technology prototype to the UI card
|
|
var currentTechControls = new Dictionary<TechnologyPrototype, Control>();
|
|
foreach (var child in container.Children)
|
|
{
|
|
if (child is MiniTechnologyCardControl)
|
|
{
|
|
currentTechControls.Add((child as MiniTechnologyCardControl)!.Technology, child);
|
|
}
|
|
}
|
|
|
|
foreach (var tech in technologies)
|
|
{
|
|
if (!currentTechControls.ContainsKey(tech))
|
|
{
|
|
// Create a card for any technology which doesn't already have one.
|
|
var mini = new MiniTechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech));
|
|
container.AddChild(mini);
|
|
}
|
|
else
|
|
{
|
|
// The tech already exists in the UI; remove it from the set, so we won't revisit it below
|
|
currentTechControls.Remove(tech);
|
|
}
|
|
}
|
|
|
|
// Now, any items left in the dictionary are technologies which were previously
|
|
// available, but now are not. Remove them.
|
|
foreach (var (tech, techControl) in currentTechControls)
|
|
{
|
|
container.Children.Remove(techControl);
|
|
}
|
|
}
|
|
}
|
|
|