using System.Linq; using Content.Client.Cargo.Systems; using Content.Client.UserInterface.Controls; using Content.Shared.Cargo; using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Prototypes; 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; using static Robust.Client.UserInterface.Controls.BaseButton; namespace Content.Client.Cargo.UI { [GenerateTypedNameReferences] public sealed partial class CargoConsoleMenu : FancyWindow { [Dependency] private readonly IGameTiming _timing = default!; private readonly IEntityManager _entityManager; private readonly IPrototypeManager _protoManager; private readonly CargoSystem _cargoSystem; private readonly SpriteSystem _spriteSystem; private EntityUid _owner; private EntityUid? _station; private readonly EntityQuery _orderConsoleQuery; private readonly EntityQuery _bankQuery; public event Action? OnItemSelected; public event Action? OnOrderApproved; public event Action? OnOrderCanceled; public event Action?, int>? OnAccountAction; public event Action? OnToggleUnboundedLimit; private readonly List _categoryStrings = new(); private string? _category; public List> ProductCatalogue = new(); public CargoConsoleMenu(EntityUid owner, IEntityManager entMan, IPrototypeManager protoManager, SpriteSystem spriteSystem) { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _entityManager = entMan; _protoManager = protoManager; _cargoSystem = entMan.System(); _spriteSystem = spriteSystem; _owner = owner; _orderConsoleQuery = _entityManager.GetEntityQuery(); _bankQuery = _entityManager.GetEntityQuery(); Title = entMan.GetComponent(owner).EntityName; SearchBar.OnTextChanged += OnSearchBarTextChanged; Categories.OnItemSelected += OnCategoryItemSelected; if (entMan.TryGetComponent(owner, out var orderConsole)) { var accountProto = _protoManager.Index(orderConsole.Account); AccountNameLabel.Text = Loc.GetString("cargo-console-menu-account-name-format", ("color", accountProto.Color), ("name", Loc.GetString(accountProto.Name)), ("code", Loc.GetString(accountProto.Code))); } TabContainer.SetTabTitle(0, Loc.GetString("cargo-console-menu-tab-title-orders")); TabContainer.SetTabTitle(1, Loc.GetString("cargo-console-menu-tab-title-funds")); ActionOptions.OnItemSelected += idx => { ActionOptions.SelectId(idx.Id); }; TransferSpinBox.IsValid = val => { if (!_entityManager.TryGetComponent(owner, out var console) || !_entityManager.TryGetComponent(_station, out var bank)) return true; return val >= 0 && val <= (int) (console.TransferLimit * bank.Accounts[console.Account]); }; AccountActionButton.OnPressed += _ => { var account = (ProtoId?) ActionOptions.SelectedMetadata; OnAccountAction?.Invoke(account, TransferSpinBox.Value); }; AccountLimitToggleButton.OnPressed += a => { OnToggleUnboundedLimit?.Invoke(a); }; } private void OnCategoryItemSelected(OptionButton.ItemSelectedEventArgs args) { SetCategoryText(args.Id); PopulateProducts(); } private void OnSearchBarTextChanged(LineEdit.LineEditEventArgs args) { PopulateProducts(); } private void SetCategoryText(int id) { _category = id == 0 ? null : _categoryStrings[id]; Categories.SelectId(id); } private IEnumerable ProductPrototypes { get { var allowedGroups = _entityManager.GetComponentOrNull(_owner)?.AllowedGroups; foreach (var cargoPrototypeId in ProductCatalogue) { var cargoPrototype = _protoManager.Index(cargoPrototypeId); if (!allowedGroups?.Contains(cargoPrototype.Group) ?? false) continue; yield return cargoPrototype; } } } /// /// Populates the list of products that will actually be shown, using the current filters. /// public void PopulateProducts() { Products.RemoveAllChildren(); var products = ProductPrototypes.ToList(); products.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase)); var search = SearchBar.Text.Trim().ToLowerInvariant(); foreach (var prototype in products) { // if no search or category // else if search // else if category and not search if (search.Length == 0 && _category == null || search.Length != 0 && prototype.Name.ToLowerInvariant().Contains(search) || search.Length != 0 && prototype.Description.ToLowerInvariant().Contains(search) || search.Length == 0 && _category != null && Loc.GetString(prototype.Category).Equals(_category)) { var button = new CargoProductRow { Product = prototype, ProductName = { Text = prototype.Name }, MainButton = { ToolTip = prototype.Description }, PointCost = { Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", prototype.Cost.ToString())) }, Icon = { Texture = _spriteSystem.Frame0(prototype.Icon) }, }; button.MainButton.OnPressed += args => { OnItemSelected?.Invoke(args); }; Products.AddChild(button); } } } /// /// Populates the list of products that will actually be shown, using the current filters. /// public void PopulateCategories() { _categoryStrings.Clear(); Categories.Clear(); foreach (var prototype in ProductPrototypes) { if (!_categoryStrings.Contains(Loc.GetString(prototype.Category))) { _categoryStrings.Add(Loc.GetString(prototype.Category)); } } _categoryStrings.Sort(); // Add "All" category at the top of the list _categoryStrings.Insert(0, Loc.GetString("cargo-console-menu-populate-categories-all-text")); foreach (var str in _categoryStrings) { Categories.AddItem(str); } } /// /// Populates the list of orders and requests. /// public void PopulateOrders(IEnumerable orders) { if (!_orderConsoleQuery.TryComp(_owner, out var orderConsole)) return; Requests.RemoveAllChildren(); foreach (var order in orders) { if (order.Approved) continue; var product = _protoManager.Index(order.ProductId); var productName = product.Name; var account = _protoManager.Index(order.Account); var row = new CargoOrderRow { Order = order, Icon = { Texture = _spriteSystem.Frame0(product) }, ProductName = { Text = Loc.GetString( "cargo-console-menu-populate-orders-cargo-order-row-product-name-text", ("productName", productName), ("orderAmount", order.OrderQuantity), ("orderRequester", order.Requester), ("accountColor", account.Color), ("account", Loc.GetString(account.Code))) }, Description = { Text = Loc.GetString("cargo-console-menu-order-reason-description", ("reason", order.Reason)) } }; row.Cancel.OnPressed += (args) => { OnOrderCanceled?.Invoke(args); }; // TODO: Disable based on access. row.SetApproveVisible(orderConsole.Mode != CargoOrderConsoleMode.SendToPrimary); row.Approve.OnPressed += (args) => { OnOrderApproved?.Invoke(args); }; Requests.AddChild(row); } } public void PopulateAccountActions() { if (!_entityManager.TryGetComponent(_station, out var bank) || !_entityManager.TryGetComponent(_owner, out var console)) return; var i = 0; ActionOptions.Clear(); ActionOptions.AddItem(Loc.GetString("cargo-console-menu-account-action-option-withdraw"), i); i++; foreach (var account in bank.Accounts.Keys) { if (account == console.Account) continue; var accountProto = _protoManager.Index(account); ActionOptions.AddItem(Loc.GetString("cargo-console-menu-account-action-option-transfer", ("code", Loc.GetString(accountProto.Code))), i); ActionOptions.SetItemMetadata(i, account); i++; } } public void UpdateStation(EntityUid station) { _station = station; } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); if (!_bankQuery.TryComp(_station, out var bankAccount) || !_orderConsoleQuery.TryComp(_owner, out var orderConsole)) { return; } var balance = _cargoSystem.GetBalanceFromAccount((_station.Value, bankAccount), orderConsole.Account); PointsLabel.Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", balance)); TransferLimitLabel.Text = Loc.GetString("cargo-console-menu-account-action-transfer-limit", ("limit", (int) (balance * orderConsole.TransferLimit))); UnlimitedNotifier.Visible = orderConsole.TransferUnbounded; AccountActionButton.Disabled = TransferSpinBox.Value <= 0 || TransferSpinBox.Value > bankAccount.Accounts[orderConsole.Account] * orderConsole.TransferLimit || _timing.CurTime < orderConsole.NextAccountActionTime; OrdersSpacer.Visible = orderConsole.Mode != CargoOrderConsoleMode.PrintSlip; Orders.Visible = orderConsole.Mode != CargoOrderConsoleMode.PrintSlip; } } }