diff --git a/Content.Client/DeltaV/Shipyard/ShipyardConsoleSystem.cs b/Content.Client/DeltaV/Shipyard/ShipyardConsoleSystem.cs new file mode 100644 index 00000000000..11847f8137d --- /dev/null +++ b/Content.Client/DeltaV/Shipyard/ShipyardConsoleSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Shipyard; + +namespace Content.Client.Shipyard; + +public sealed class ShipyardConsoleSystem : SharedShipyardConsoleSystem; diff --git a/Content.Client/DeltaV/Shipyard/UI/ShipyardBoundUserInterface.cs b/Content.Client/DeltaV/Shipyard/UI/ShipyardBoundUserInterface.cs new file mode 100644 index 00000000000..4a3def491e3 --- /dev/null +++ b/Content.Client/DeltaV/Shipyard/UI/ShipyardBoundUserInterface.cs @@ -0,0 +1,56 @@ +using Content.Shared.Access.Systems; +using Content.Shared.Shipyard; +using Robust.Client.GameObjects; +using Robust.Client.Player; +using Robust.Shared.Prototypes; + +namespace Content.Client.DeltaV.Shipyard.UI; + +public sealed class ShipyardConsoleBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + private readonly AccessReaderSystem _access; + + [ViewVariables] + private ShipyardConsoleMenu? _menu; + + public ShipyardConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _access = EntMan.System(); + } + + protected override void Open() + { + base.Open(); + + _menu = new ShipyardConsoleMenu(Owner, _proto, EntMan, _player, _access); + _menu.OpenCentered(); + _menu.OnClose += Close; + _menu.OnPurchased += Purchase; + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not ShipyardConsoleState cast) + return; + + _menu?.UpdateState(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + _menu?.Dispose(); + } + + private void Purchase(string id) + { + SendMessage(new ShipyardConsolePurchaseMessage(id)); + } +} diff --git a/Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml b/Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml new file mode 100644 index 00000000000..9eccd45b698 --- /dev/null +++ b/Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml @@ -0,0 +1,29 @@ + + + + diff --git a/Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml.cs b/Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml.cs new file mode 100644 index 00000000000..6821b066ff2 --- /dev/null +++ b/Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml.cs @@ -0,0 +1,110 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Access.Systems; +using Content.Shared.Shipyard; +using Content.Shared.Shipyard.Prototypes; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.Player; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client.DeltaV.Shipyard.UI; + +[GenerateTypedNameReferences] +public sealed partial class ShipyardConsoleMenu : FancyWindow +{ + private readonly AccessReaderSystem _access; + private readonly IPlayerManager _player; + + public event Action? OnPurchased; + + private readonly List _vessels = new(); + private readonly List _categories = new(); + + public Entity Console; + private string? _category; + + public ShipyardConsoleMenu(EntityUid console, IPrototypeManager proto, IEntityManager entMan, IPlayerManager player, AccessReaderSystem access) + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + Console = (console, entMan.GetComponent(console)); + _access = access; + _player = player; + + // don't include ships that aren't allowed by whitelist, server won't accept them anyway + foreach (var vessel in proto.EnumeratePrototypes()) + { + if (vessel.Whitelist?.IsValid(console, entMan) != false) + _vessels.Add(vessel); + } + _vessels.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase)); + + // only list categories in said ships + foreach (var vessel in _vessels) + { + foreach (var category in vessel.Categories) + { + if (!_categories.Contains(category)) + _categories.Add(category); + } + } + + _categories.Sort(); + // inserting here and not adding at the start so it doesn't get affected by sort + _categories.Insert(0, Loc.GetString("cargo-console-menu-populate-categories-all-text")); + PopulateCategories(); + + SearchBar.OnTextChanged += _ => PopulateProducts(); + Categories.OnItemSelected += args => + { + _category = args.Id == 0 ? null : _categories[args.Id]; + Categories.SelectId(args.Id); + PopulateProducts(); + }; + } + + /// + /// Populates the list of products that will actually be shown, using the current filters. + /// + private void PopulateProducts() + { + Vessels.RemoveAllChildren(); + + var access = _player.LocalSession?.AttachedEntity is {} player + && _access.IsAllowed(player, Console); + + var search = SearchBar.Text.Trim().ToLowerInvariant(); + foreach (var vessel in _vessels) + { + if (search.Length != 0 && !vessel.Name.ToLowerInvariant().Contains(search)) + continue; + if (_category != null && !vessel.Categories.Contains(_category)) + continue; + + var vesselEntry = new VesselRow(vessel, access); + vesselEntry.OnPurchasePressed += () => OnPurchased?.Invoke(vessel.ID); + Vessels.AddChild(vesselEntry); + } + } + + /// + /// Populates the list categories that will actually be shown, using the current filters. + /// + private void PopulateCategories() + { + Categories.Clear(); + foreach (var category in _categories) + { + Categories.AddItem(category); + } + } + + public void UpdateState(ShipyardConsoleState state) + { + BankAccountLabel.Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", state.Balance.ToString())); + PopulateProducts(); + } +} diff --git a/Content.Client/DeltaV/Shipyard/UI/VesselRow.xaml b/Content.Client/DeltaV/Shipyard/UI/VesselRow.xaml new file mode 100644 index 00000000000..eac2d3a1bde --- /dev/null +++ b/Content.Client/DeltaV/Shipyard/UI/VesselRow.xaml @@ -0,0 +1,16 @@ + + +