From 1580750606e5a8565154b6f4568d72e7c1cab54a Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Thu, 21 Nov 2019 16:37:15 -0800 Subject: [PATCH] Implement Cargo Console (#413) * Implement Cargo Console Add to CargoConsoleComponent GalacticBank information for syncing Bank Account Balance. Implement CargoOrderDatabase on the server side and a list of orders in the CargoOrderDatabaseComponent on the client side. This makes it easier to change data on the server side but also utilize the state syncing between components. Implement GalacticMarketComponent. Only productIds get sent. Both client and server create their lists from YAML. Implement basic spawning of items from approved orders in CargoOrderDatabase. * Finish Cargo Console Add validation to make sure Order Amount is one or more. Implement approve and cancel buttons to CargoConsoleMenu orders list row. Add price to CargoConsoleMenu product list row. Implement CargoOrderDataManager to consolidate CargoOrder lists. Refactor CargoOrderDatabaseComponent to use CargoOrderDataManager instead of storing duplicate lists. Implement canceling orders. Implement approving orders. Fix sprite links. Implement Cargo Request Console. --- Content.Client/EntryPoint.cs | 3 +- .../Cargo/CargoConsoleBoundUserInterface.cs | 132 +++++ .../Cargo/CargoOrderDatabaseComponent.cs | 56 +++ .../Cargo/GalacticMarketComponent.cs | 38 ++ .../UserInterface/Cargo/CargoConsoleMenu.cs | 465 ++++++++++++++++++ .../Cargo/CargoConsoleOrderMenu.cs | 66 +++ .../Cargo/GalacticBankSelectionMenu.cs | 69 +++ Content.Client/UserInterface/NanoStyle.cs | 35 +- Content.Server/Cargo/CargoBankAccount.cs | 20 + Content.Server/Cargo/CargoOrderDataManager.cs | 99 ++++ Content.Server/Cargo/CargoOrderDatabase.cs | 104 ++++ Content.Server/Cargo/GalacticBankManager.cs | 109 ++++ Content.Server/Cargo/ICargoBankAccount.cs | 11 + .../Cargo/ICargoOrderDataManager.cs | 17 + Content.Server/Cargo/IGalacticBankManager.cs | 20 + Content.Server/EntryPoint.cs | 13 +- .../Components/Cargo/CargoConsoleComponent.cs | 133 +++++ .../Cargo/CargoOrderDatabaseComponent.cs | 30 ++ .../Cargo/GalacticMarketComponent.cs | 19 + .../Cargo/SharedCargoConsoleComponent.cs | 106 ++++ .../SharedCargoOrderDatabaseComponent.cs | 25 + .../Cargo/SharedGalacticMarketComponent.cs | 97 ++++ Content.Shared/GameObjects/ContentNetIDs.cs | 2 + .../Prototypes/Cargo/CargoOrderData.cs | 34 ++ .../Prototypes/Cargo/CargoProductPrototype.cs | 111 +++++ Resources/Maps/stationstation.yml | 4 +- Resources/Prototypes/Cargo/products.yml | 33 ++ .../Entities/buildings/computers.yml | 26 +- RobustToolbox | 2 +- 29 files changed, 1867 insertions(+), 12 deletions(-) create mode 100644 Content.Client/GameObjects/Components/Cargo/CargoConsoleBoundUserInterface.cs create mode 100644 Content.Client/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs create mode 100644 Content.Client/GameObjects/Components/Cargo/GalacticMarketComponent.cs create mode 100644 Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs create mode 100644 Content.Client/UserInterface/Cargo/CargoConsoleOrderMenu.cs create mode 100644 Content.Client/UserInterface/Cargo/GalacticBankSelectionMenu.cs create mode 100644 Content.Server/Cargo/CargoBankAccount.cs create mode 100644 Content.Server/Cargo/CargoOrderDataManager.cs create mode 100644 Content.Server/Cargo/CargoOrderDatabase.cs create mode 100644 Content.Server/Cargo/GalacticBankManager.cs create mode 100644 Content.Server/Cargo/ICargoBankAccount.cs create mode 100644 Content.Server/Cargo/ICargoOrderDataManager.cs create mode 100644 Content.Server/Cargo/IGalacticBankManager.cs create mode 100644 Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs create mode 100644 Content.Server/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs create mode 100644 Content.Server/GameObjects/Components/Cargo/GalacticMarketComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Cargo/SharedCargoConsoleComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Cargo/SharedCargoOrderDatabaseComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Cargo/SharedGalacticMarketComponent.cs create mode 100644 Content.Shared/Prototypes/Cargo/CargoOrderData.cs create mode 100644 Content.Shared/Prototypes/Cargo/CargoProductPrototype.cs create mode 100644 Resources/Prototypes/Cargo/products.yml diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 865ab6fd31..4ecc6e6a73 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -10,6 +10,7 @@ using Content.Client.Parallax; using Content.Client.Sandbox; using Content.Client.UserInterface; using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Cargo; using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Markers; using Content.Shared.GameObjects.Components.Research; @@ -136,7 +137,7 @@ namespace Content.Client factory.Register(); factory.Register(); - + factory.Register(); factory.Register(); prototypes.RegisterIgnore("material"); diff --git a/Content.Client/GameObjects/Components/Cargo/CargoConsoleBoundUserInterface.cs b/Content.Client/GameObjects/Components/Cargo/CargoConsoleBoundUserInterface.cs new file mode 100644 index 0000000000..d98a89173b --- /dev/null +++ b/Content.Client/GameObjects/Components/Cargo/CargoConsoleBoundUserInterface.cs @@ -0,0 +1,132 @@ +using Content.Client.UserInterface.Cargo; +using Content.Shared.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; +using Robust.Client.GameObjects.Components.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.ViewVariables; + +namespace Content.Client.GameObjects.Components.Cargo +{ + public class CargoConsoleBoundUserInterface : BoundUserInterface + { + [ViewVariables] + private CargoConsoleMenu _menu; + [ViewVariables] + private CargoConsoleOrderMenu _orderMenu; + + [ViewVariables] + public GalacticMarketComponent Market { get; private set; } + [ViewVariables] + public CargoOrderDatabaseComponent Orders { get; private set; } + [ViewVariables] + public bool RequestOnly { get; private set; } + [ViewVariables] + public int BankId { get; private set; } + [ViewVariables] + public string BankName { get; private set; } + [ViewVariables] + public int BankBalance { get; private set; } + + private CargoProductPrototype _product; + + public CargoConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + if (!Owner.Owner.TryGetComponent(out GalacticMarketComponent market) + || !Owner.Owner.TryGetComponent(out CargoOrderDatabaseComponent orders)) return; + + Market = market; + Orders = orders; + + _menu = new CargoConsoleMenu(this); + _orderMenu = new CargoConsoleOrderMenu(); + + _menu.OnClose += Close; + + _menu.Populate(); + + Market.OnDatabaseUpdated += _menu.PopulateProducts; + Market.OnDatabaseUpdated += _menu.PopulateCategories; + Orders.OnDatabaseUpdated += _menu.PopulateOrders; + + _menu.CallShuttleButton.OnPressed += (args) => + { + SendMessage(new SharedCargoConsoleComponent.CargoConsoleShuttleMessage()); + }; + _menu.OnItemSelected += (args) => + { + if (!(args.Button.Parent is CargoProductRow row)) + return; + _product = row.Product; + _orderMenu.Requester.Text = null; + _orderMenu.Reason.Text = null; + _orderMenu.Amount.Value = 1; + _orderMenu.OpenCenteredMinSize(); + }; + _menu.OnOrderApproved += ApproveOrder; + _menu.OnOrderCanceled += RemoveOrder; + _orderMenu.SubmitButton.OnPressed += (args) => + { + AddOrder(); + _orderMenu.Close(); + }; + + _menu.OpenCentered(); + + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (!(state is CargoConsoleInterfaceState cstate)) + return; + if (RequestOnly != cstate.RequestOnly) + { + RequestOnly = cstate.RequestOnly; + _menu.UpdateRequestOnly(); + } + BankId = cstate.BankId; + BankName = cstate.BankName; + BankBalance = cstate.BankBalance; + _menu.UpdateBankData(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) return; + Market.OnDatabaseUpdated -= _menu.PopulateProducts; + Market.OnDatabaseUpdated -= _menu.PopulateCategories; + Orders.OnDatabaseUpdated -= _menu.PopulateOrders; + _menu?.Dispose(); + _orderMenu?.Dispose(); + } + + internal void AddOrder() + { + SendMessage(new SharedCargoConsoleComponent.CargoConsoleAddOrderMessage(_orderMenu.Requester.Text, + _orderMenu.Reason.Text, _product.ID, _orderMenu.Amount.Value)); + } + + internal void RemoveOrder(BaseButton.ButtonEventArgs args) + { + if (!(args.Button.Parent.Parent is CargoOrderRow row)) + return; + SendMessage(new SharedCargoConsoleComponent.CargoConsoleRemoveOrderMessage(row.Order.OrderNumber)); + } + + internal void ApproveOrder(BaseButton.ButtonEventArgs args) + { + if (!(args.Button.Parent.Parent is CargoOrderRow row)) + return; + SendMessage(new SharedCargoConsoleComponent.CargoConsoleApproveOrderMessage(row.Order.OrderNumber)); + } + } +} diff --git a/Content.Client/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs b/Content.Client/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs new file mode 100644 index 0000000000..18b729b8ca --- /dev/null +++ b/Content.Client/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs @@ -0,0 +1,56 @@ +using Content.Shared.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; +using Robust.Shared.GameObjects; +using System; +using System.Collections.Generic; + +namespace Content.Client.GameObjects.Components.Cargo +{ + [RegisterComponent] + public class CargoOrderDatabaseComponent : SharedCargoOrderDatabaseComponent + { + private List _orders = new List(); + + public IReadOnlyList Orders => _orders; + /// + /// Event called when the database is updated. + /// + public event Action OnDatabaseUpdated; + + // TODO add account selector menu + + /// + /// Removes all orders from the database. + /// + public virtual void Clear() + { + _orders.Clear(); + } + + /// + /// Adds an order to the database. + /// + /// The order to be added. + public virtual void AddOrder(CargoOrderData order) + { + if (!_orders.Contains(order)) + _orders.Add(order); + } + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + base.HandleComponentState(curState, nextState); + if (!(curState is CargoOrderDatabaseState state)) + return; + Clear(); + if (state.Orders == null) + return; + foreach (var order in state.Orders) + { + AddOrder(order); + } + + OnDatabaseUpdated?.Invoke(); + } + } +} diff --git a/Content.Client/GameObjects/Components/Cargo/GalacticMarketComponent.cs b/Content.Client/GameObjects/Components/Cargo/GalacticMarketComponent.cs new file mode 100644 index 0000000000..bb29dfd1d4 --- /dev/null +++ b/Content.Client/GameObjects/Components/Cargo/GalacticMarketComponent.cs @@ -0,0 +1,38 @@ +using Content.Shared.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using System; + +namespace Content.Client.GameObjects.Components.Cargo +{ + [RegisterComponent] + public class GalacticMarketComponent : SharedGalacticMarketComponent + { +#pragma warning disable CS0649 + [Dependency] private IPrototypeManager _prototypeManager; +#pragma warning restore + + /// + /// Event called when the database is updated. + /// + public event Action OnDatabaseUpdated; + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + base.HandleComponentState(curState, nextState); + if (!(curState is GalacticMarketState state)) + return; + _products.Clear(); + foreach (var productId in state.Products) + { + if (!_prototypeManager.TryIndex(productId, out CargoProductPrototype product)) + continue; + _products.Add(product); + } + + OnDatabaseUpdated?.Invoke(); + } + } +} diff --git a/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs b/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs new file mode 100644 index 0000000000..9db6b0a7ac --- /dev/null +++ b/Content.Client/UserInterface/Cargo/CargoConsoleMenu.cs @@ -0,0 +1,465 @@ +using Content.Client.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; +using Robust.Client.Graphics; +using Robust.Client.Graphics.Drawing; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.Utility; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Utility; +using System; +using System.Collections.Generic; +using Content.Client.Utility; + +namespace Content.Client.UserInterface.Cargo +{ + public class CargoConsoleMenu : SS14Window + { +#pragma warning disable 649 + [Dependency] private readonly ILocalizationManager _loc; + [Dependency] private readonly IResourceCache _resourceCache; +#pragma warning restore 649 + + protected override Vector2? CustomSize => (400, 600); + + public CargoConsoleBoundUserInterface Owner { get; private set; } + + public event Action OnItemSelected; + public event Action OnOrderApproved; + public event Action OnOrderCanceled; + + private List _categoryStrings = new List(); + + private Label _accountNameLabel { get; set; } + private Label _pointsLabel { get; set; } + private Label _shuttleStatusLabel { get; set; } + private VBoxContainer _requests { get; set; } + private VBoxContainer _orders { get; set; } + private OptionButton _categories { get; set; } + private LineEdit _searchBar { get; set; } + + public VBoxContainer Products { get; set; } + public Button CallShuttleButton { get; set; } + public Button PermissionsButton { get; set; } + + private string _category = null; + + public CargoConsoleMenu(CargoConsoleBoundUserInterface owner) + { + IoCManager.InjectDependencies(this); + Owner = owner; + + if (Owner.RequestOnly) + Title = _loc.GetString("Cargo Request Console"); + else + Title = _loc.GetString("Cargo Shuttle Console"); + + var rows = new VBoxContainer + { + MarginTop = 0 + }; + + var accountName = new HBoxContainer() + { + MarginTop = 0 + }; + var accountNameLabel = new Label { + Text = _loc.GetString("Account Name: "), + StyleClasses = { NanoStyle.StyleClassLabelKeyText } + }; + _accountNameLabel = new Label { + Text = "None" //Owner.Bank.Account.Name + }; + accountName.AddChild(accountNameLabel); + accountName.AddChild(_accountNameLabel); + rows.AddChild(accountName); + + var points = new HBoxContainer(); + var pointsLabel = new Label + { + Text = _loc.GetString("Points: "), + StyleClasses = { NanoStyle.StyleClassLabelKeyText } + }; + _pointsLabel = new Label + { + Text = "0" //Owner.Bank.Account.Balance.ToString() + }; + points.AddChild(pointsLabel); + points.AddChild(_pointsLabel); + rows.AddChild(points); + + var shuttleStatus = new HBoxContainer(); + var shuttleStatusLabel = new Label + { + Text = _loc.GetString("Shuttle Status: "), + StyleClasses = { NanoStyle.StyleClassLabelKeyText } + }; + _shuttleStatusLabel = new Label + { + Text = _loc.GetString("Away") // Shuttle.Status + }; + shuttleStatus.AddChild(shuttleStatusLabel); + shuttleStatus.AddChild(_shuttleStatusLabel); + rows.AddChild(shuttleStatus); + + var buttons = new HBoxContainer(); + CallShuttleButton = new Button() + { + Text = _loc.GetString("Call Shuttle"), + TextAlign = Button.AlignMode.Center, + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + PermissionsButton = new Button() + { + Text = _loc.GetString("Permissions"), + TextAlign = Button.AlignMode.Center + }; + buttons.AddChild(CallShuttleButton); + buttons.AddChild(PermissionsButton); + rows.AddChild(buttons); + + var category = new HBoxContainer(); + _categories = new OptionButton + { + Prefix = _loc.GetString("Categories: "), + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsStretchRatio = 1 + }; + _searchBar = new LineEdit + { + PlaceHolder = _loc.GetString("Search"), + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsStretchRatio = 1 + }; + category.AddChild(_categories); + category.AddChild(_searchBar); + rows.AddChild(category); + + var products = new ScrollContainer() + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsStretchRatio = 6 + }; + Products = new VBoxContainer() + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.FillExpand + }; + products.AddChild(Products); + rows.AddChild(products); + + var requestsAndOrders = new PanelContainer + { + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsStretchRatio = 6, + PanelOverride = new StyleBoxFlat { BackgroundColor = Color.Black } + }; + var orderScrollBox = new ScrollContainer + { + SizeFlagsVertical = SizeFlags.FillExpand + }; + var rAndOVBox = new VBoxContainer(); + var requestsLabel = new Label { Text = _loc.GetString("Requests") }; + _requests = new VBoxContainer // replace with scroll box so that approval buttons can be added + { + StyleClasses = { "transparentItemList" }, + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsStretchRatio = 1, + }; + var ordersLabel = new Label { Text = _loc.GetString("Orders") }; + _orders = new VBoxContainer + { + StyleClasses = { "transparentItemList" }, + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsStretchRatio = 1, + }; + rAndOVBox.AddChild(requestsLabel); + rAndOVBox.AddChild(_requests); + rAndOVBox.AddChild(ordersLabel); + rAndOVBox.AddChild(_orders); + orderScrollBox.AddChild(rAndOVBox); + requestsAndOrders.AddChild(orderScrollBox); + rows.AddChild(requestsAndOrders); + + rows.AddChild(new TextureButton + { + SizeFlagsVertical = SizeFlags.FillExpand, + }); + Contents.AddChild(rows); + + CallShuttleButton.OnPressed += OnCallShuttleButtonPressed; + _searchBar.OnTextChanged += OnSearchBarTextChanged; + _categories.OnItemSelected += OnCategoryItemSelected; + } + + private void OnCallShuttleButtonPressed(BaseButton.ButtonEventArgs args) + { + } + + private void OnCategoryItemSelected(OptionButton.ItemSelectedEventArgs args) + { + SetCategoryText(args.Id); + PopulateProducts(); + } + + private void OnSearchBarTextChanged(LineEdit.LineEditEventArgs args) + { + PopulateProducts(); + } + + private void SetCategoryText(int id) + { + if (id == 0) + _category = null; + else + _category = _categoryStrings[id]; + _categories.SelectId(id); + } + + /// + /// Populates the list of products that will actually be shown, using the current filters. + /// + public void PopulateProducts() + { + Products.RemoveAllChildren(); + + var search = _searchBar.Text.Trim().ToLowerInvariant(); + foreach (var prototype in Owner.Market.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 && _category != null && prototype.Category.Equals(_category))) + { + var button = new CargoProductRow(); + button.Product = prototype; + button.ProductName.Text = prototype.Name; + button.PointCost.Text = prototype.PointCost.ToString(); + button.Icon.Texture = prototype.Icon.Frame0(); + 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(); + + _categoryStrings.Add(_loc.GetString("All")); + + var search = _searchBar.Text.Trim().ToLowerInvariant(); + foreach (var prototype in Owner.Market.Products) + { + if (!_categoryStrings.Contains(prototype.Category)) + { + _categoryStrings.Add(_loc.GetString(prototype.Category)); + } + } + _categoryStrings.Sort(); + foreach (var str in _categoryStrings) + { + _categories.AddItem(str); + } + } + + /// + /// Populates the list of orders and requests. + /// + public void PopulateOrders() + { + _orders.RemoveAllChildren(); + _requests.RemoveAllChildren(); + + foreach (var order in Owner.Orders.Orders) + { + var row = new CargoOrderRow(); + row.Order = order; + row.Icon.Texture = Owner.Market.GetProduct(order.ProductId).Icon.Frame0(); + row.ProductName.Text = $"{Owner.Market.GetProduct(order.ProductId).Name} (x{order.Amount}) by {order.Requester}"; + row.Description.Text = $"Reasons: {order.Reason}"; + row.Cancel.OnPressed += (args) => { OnOrderCanceled?.Invoke(args); }; + if (order.Approved) + { + row.Approve.Visible = false; + row.Cancel.Visible = false; + _orders.AddChild(row); + } + else + { + if (Owner.RequestOnly) + row.Approve.Visible = false; + else + row.Approve.OnPressed += (args) => { OnOrderApproved?.Invoke(args); }; + _requests.AddChild(row); + } + } + } + + public void Populate() + { + PopulateProducts(); + PopulateCategories(); + PopulateOrders(); + } + + public void UpdateBankData() + { + _accountNameLabel.Text = Owner.BankName; + _pointsLabel.Text = Owner.BankBalance.ToString(); + } + + /// + /// Show/Hide Call Shuttle button and Approve buttons + /// + public void UpdateRequestOnly() + { + CallShuttleButton.Visible = !Owner.RequestOnly; + foreach (CargoOrderRow row in _requests.Children) + { + row.Approve.Visible = !Owner.RequestOnly; + } + } + } + + internal class CargoProductRow : PanelContainer + { + public CargoProductPrototype Product { get; set; } + public TextureRect Icon { get; private set; } + public Button MainButton { get; private set; } + public Label ProductName { get; private set; } + public Label PointCost { get; private set; } + + public CargoProductRow() + { + SizeFlagsHorizontal = SizeFlags.FillExpand; + + MainButton = new Button + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.FillExpand + }; + AddChild(MainButton); + + var hBox = new HBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + MouseFilter = MouseFilterMode.Ignore + }; + + Icon = new TextureRect + { + CustomMinimumSize = new Vector2(32.0f, 32.0f), + MouseFilter = MouseFilterMode.Ignore, + RectClipContent = true + }; + hBox.AddChild(Icon); + + ProductName = new Label + { + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + hBox.AddChild(ProductName); + + var panel = new PanelContainer + { + PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(37, 37, 42) }, + MouseFilter = MouseFilterMode.Ignore + }; + PointCost = new Label + { + CustomMinimumSize = new Vector2(40.0f, 32.0f), + Align = Label.AlignMode.Right + }; + panel.AddChild(PointCost); + hBox.AddChild(panel); + + AddChild(hBox); + } + } + + internal class CargoOrderRow : PanelContainer + { +#pragma warning disable 649 + [Dependency] readonly IResourceCache _resourceCache; +#pragma warning restore 649 + + public CargoOrderData Order { get; set; } + public TextureRect Icon { get; private set; } + public Label ProductName { get; private set; } + public Label Description { get; private set; } + public BaseButton Approve { get; private set; } + public BaseButton Cancel { get; private set; } + + public CargoOrderRow() + { + SizeFlagsHorizontal = SizeFlags.FillExpand; + + var hBox = new HBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + MouseFilter = MouseFilterMode.Ignore + }; + + Icon = new TextureRect + { + CustomMinimumSize = new Vector2(32.0f, 32.0f), + MouseFilter = MouseFilterMode.Ignore, + RectClipContent = true + }; + hBox.AddChild(Icon); + + var vBox = new VBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.FillExpand + }; + ProductName = new Label + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + StyleClasses = { NanoStyle.StyleClassLabelSubText }, + ClipText = true + }; + Description = new Label + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + StyleClasses = { NanoStyle.StyleClassLabelSubText }, + ClipText = true + }; + vBox.AddChild(ProductName); + vBox.AddChild(Description); + hBox.AddChild(vBox); + + Approve = new Button + { + Text = "Approve", + StyleClasses = { NanoStyle.StyleClassLabelSubText } + }; + hBox.AddChild(Approve); + + Cancel = new Button + { + Text = "Cancel", + StyleClasses = { NanoStyle.StyleClassLabelSubText } + }; + hBox.AddChild(Cancel); + + AddChild(hBox); + } + } +} diff --git a/Content.Client/UserInterface/Cargo/CargoConsoleOrderMenu.cs b/Content.Client/UserInterface/Cargo/CargoConsoleOrderMenu.cs new file mode 100644 index 0000000000..5a90e37850 --- /dev/null +++ b/Content.Client/UserInterface/Cargo/CargoConsoleOrderMenu.cs @@ -0,0 +1,66 @@ +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using System; +using System.Collections.Generic; + +namespace Content.Client.UserInterface.Cargo +{ + class CargoConsoleOrderMenu : SS14Window + { +#pragma warning disable 649 + [Dependency] private readonly ILocalizationManager _loc; +#pragma warning restore 649 + + public LineEdit Requester { get; set; } + public LineEdit Reason { get; set; } + public SpinBox Amount { get; set; } + public Button SubmitButton { get; set; } + + public CargoConsoleOrderMenu() + { + IoCManager.InjectDependencies(this); + + Title = _loc.GetString("Order Form"); + + var vBox = new VBoxContainer(); + var gridContainer = new GridContainer { Columns = 2 }; + + var requesterLabel = new Label { Text = _loc.GetString("Name:") }; + Requester = new LineEdit(); + gridContainer.AddChild(requesterLabel); + gridContainer.AddChild(Requester); + + var reasonLabel = new Label { Text = _loc.GetString("Reason:") }; + Reason = new LineEdit(); + gridContainer.AddChild(reasonLabel); + gridContainer.AddChild(Reason); + + var amountLabel = new Label { Text = _loc.GetString("Amount:") }; + Amount = new SpinBox + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Value = 1 + }; + Amount.SetButtons(new List() { -100, -10, -1 }, new List() { 1, 10, 100 }); + Amount.IsValid = (n) => { + return (n > 0); + }; + gridContainer.AddChild(amountLabel); + gridContainer.AddChild(Amount); + + vBox.AddChild(gridContainer); + + SubmitButton = new Button() + { + Text = _loc.GetString("OK"), + TextAlign = Button.AlignMode.Center, + }; + vBox.AddChild(SubmitButton); + + Contents.AddChild(vBox); + } + } +} diff --git a/Content.Client/UserInterface/Cargo/GalacticBankSelectionMenu.cs b/Content.Client/UserInterface/Cargo/GalacticBankSelectionMenu.cs new file mode 100644 index 0000000000..28ad546ddd --- /dev/null +++ b/Content.Client/UserInterface/Cargo/GalacticBankSelectionMenu.cs @@ -0,0 +1,69 @@ + +using Content.Client.GameObjects.Components.Cargo; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using System; +using static Robust.Client.UserInterface.Controls.ItemList; + +namespace Content.Client.UserInterface.Cargo +{ + public class GalacticBankSelectionMenu : SS14Window + { + private ItemList _accounts; + private int _accountCount = 0; + private string[] _accountNames = new string[] { }; + private int[] _accountIds = new int[] { }; + private int _selectedAccountId = -1; + +#pragma warning disable 649 + [Dependency] private readonly ILocalizationManager _loc; +#pragma warning restore 649 + + protected override Vector2? CustomSize => (300, 300); + + public CargoConsoleBoundUserInterface Owner; + + public GalacticBankSelectionMenu() + { + IoCManager.InjectDependencies(this); + + Title = _loc.GetString("Galactic Bank Selection"); + + _accounts = new ItemList() { SelectMode = ItemList.ItemListSelectMode.Single }; + + var margin = new MarginContainer() + { + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsHorizontal = SizeFlags.FillExpand, + MarginTop = 5f, + MarginLeft = 5f, + MarginRight = -5f, + MarginBottom = -5f, + }; + + margin.AddChild(_accounts); + + Contents.AddChild(margin); + } + + public void Populate(int accountCount, string[] accountNames, int[] accountIds, int selectedAccountId) + { + _accountCount = accountCount; + _accountNames = accountNames; + _accountIds = accountIds; + _selectedAccountId = selectedAccountId; + + _accounts.Clear(); + for (var i = 0; i < _accountCount; i++) + { + var id = _accountIds[i]; + _accounts.AddItem($"ID: {id} || {_accountNames[i]}"); + if (id == _selectedAccountId) + _accounts[id].Selected = true; + } + } + } +} diff --git a/Content.Client/UserInterface/NanoStyle.cs b/Content.Client/UserInterface/NanoStyle.cs index e39316414a..a76adff12d 100644 --- a/Content.Client/UserInterface/NanoStyle.cs +++ b/Content.Client/UserInterface/NanoStyle.cs @@ -15,6 +15,7 @@ namespace Content.Client.UserInterface public const string StyleClassLabelHeading = "LabelHeading"; public const string StyleClassLabelHeadingBigger = "LabelHeadingBigger"; public const string StyleClassLabelSubText = "LabelSubText"; + public const string StyleClassLabelKeyText = "LabelKeyText"; public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor"; public const string StyleClassLabelBig = "LabelBig"; public const string StyleClassButtonBig = "ButtonBig"; @@ -36,6 +37,7 @@ namespace Content.Client.UserInterface var resCache = IoCManager.Resolve(); var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10); var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12); + var notoSansBold12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 12); var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14); var notoSans16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 16); var notoSansBold16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 16); @@ -170,6 +172,9 @@ namespace Content.Client.UserInterface var itemListItemBackground = new StyleBoxFlat {BackgroundColor = new Color(55, 55, 68)}; itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4); + var itemListItemBackgroundTransparent = new StyleBoxFlat { BackgroundColor = Color.Transparent }; + itemListItemBackgroundTransparent.SetContentMarginOverride(StyleBox.Margin.Vertical, 2); + itemListItemBackgroundTransparent.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4); // NanoHeading var nanoHeadingTex = resCache.GetTexture("/Nano/nanoheading.svg.96dpi.png"); @@ -429,6 +434,18 @@ namespace Content.Client.UserInterface itemListBackgroundSelected) }), + new StyleRule(new SelectorElement(typeof(ItemList), new []{"transparentItemList"}, null, null), new[] + { + new StyleProperty(ItemList.StylePropertyBackground, + new StyleBoxFlat {BackgroundColor = Color.Transparent}), + new StyleProperty(ItemList.StylePropertyItemBackground, + itemListItemBackgroundTransparent), + new StyleProperty(ItemList.StylePropertyDisabledItemBackground, + itemListItemBackgroundDisabled), + new StyleProperty(ItemList.StylePropertySelectedItemBackground, + itemListBackgroundSelected) + }), + // Tree new StyleRule(new SelectorElement(typeof(Tree), null, null, null), new[] { @@ -475,12 +492,18 @@ namespace Content.Client.UserInterface new StyleProperty(Label.StylePropertyFontColor, Color.DarkGray), }), - new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassLabelSecondaryColor}, null, null), - new[] - { - new StyleProperty(Label.StylePropertyFont, notoSans12), - new StyleProperty(Label.StylePropertyFontColor, Color.DarkGray), - }), + // Label Key + new StyleRule(new SelectorElement(typeof(Label), new []{StyleClassLabelKeyText}, null, null), new [] + { + new StyleProperty(Label.StylePropertyFont, notoSansBold12), + new StyleProperty(Label.StylePropertyFontColor, NanoGold) + }), + + new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassLabelSecondaryColor}, null, null), new[] + { + new StyleProperty(Label.StylePropertyFont, notoSans12), + new StyleProperty(Label.StylePropertyFontColor, Color.DarkGray), + }), // Big Button new StyleRule(new SelectorElement(typeof(Button), new[] {StyleClassButtonBig}, null, null), new[] diff --git a/Content.Server/Cargo/CargoBankAccount.cs b/Content.Server/Cargo/CargoBankAccount.cs new file mode 100644 index 0000000000..a7244f4ed7 --- /dev/null +++ b/Content.Server/Cargo/CargoBankAccount.cs @@ -0,0 +1,20 @@ +using System; + +namespace Content.Server.Cargo +{ + public class CargoBankAccount : ICargoBankAccount + { + public int Id { get; } + + public string Name { get; } + + public int Balance { get; set; } + + public CargoBankAccount(int id, string name, int balance) + { + Id = id; + Name = name; + Balance = balance; + } + } +} diff --git a/Content.Server/Cargo/CargoOrderDataManager.cs b/Content.Server/Cargo/CargoOrderDataManager.cs new file mode 100644 index 0000000000..a9b05dba06 --- /dev/null +++ b/Content.Server/Cargo/CargoOrderDataManager.cs @@ -0,0 +1,99 @@ +using Content.Server.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; +using System; +using System.Collections.Generic; + +namespace Content.Server.Cargo +{ + public class CargoOrderDataManager : ICargoOrderDataManager + { + private readonly Dictionary _accounts = new Dictionary(); + private readonly List _components = new List(); + + public CargoOrderDataManager() + { + CreateAccount(0); + } + + public void CreateAccount(int id) + { + _accounts.Add(id, new CargoOrderDatabase(id)); + } + + public bool TryGetAccount(int id, out CargoOrderDatabase account) + { + if (_accounts.TryGetValue(id, out var _account)) + { + account = _account; + return true; + } + account = null; + return false; + } + + /// + /// Adds an order to the database. + /// + /// The person who requested the item. + /// The reason the product was requested. + /// The ID of the product requested. + /// The amount of the products requested. + /// The ID of the bank account paying for the order. + /// Whether the order will be bought when the orders are processed. + public virtual void AddOrder(int id, string requester, string reason, string productId, int amount, int payingAccountId) + { + if (amount < 1 || !TryGetAccount(id, out var account)) + return; + account.AddOrder(requester, reason, productId, amount, payingAccountId); + SyncComponentsWithId(id); + } + + public void RemoveOrder(int id, int orderNumber) + { + if (!TryGetAccount(id, out var account)) + return; + account.RemoveOrder(orderNumber); + SyncComponentsWithId(id); + } + + public void ApproveOrder(int id, int orderNumber) + { + if (!TryGetAccount(id, out var account)) + return; + account.ApproveOrder(orderNumber); + SyncComponentsWithId(id); + } + + private void SyncComponentsWithId(int id) + { + foreach (var component in _components) + { + if (!component.ConnectedToDatabase || component.Database.Id != id) + continue; + component.Dirty(); + } + } + + public List RemoveAndGetApprovedFrom(CargoOrderDatabase database) + { + var approvedOrders = database.SpliceApproved(); + SyncComponentsWithId(database.Id); + return approvedOrders; + } + + public void AddComponent(CargoOrderDatabaseComponent component) + { + if (_components.Contains(component)) + return; + _components.Add(component); + component.Database = _accounts[0]; + } + + public List GetOrdersFromAccount(int accountId) + { + if (!TryGetAccount(accountId, out var account)) + return null; + return account.GetOrders(); + } + } +} diff --git a/Content.Server/Cargo/CargoOrderDatabase.cs b/Content.Server/Cargo/CargoOrderDatabase.cs new file mode 100644 index 0000000000..9f8ee35ad5 --- /dev/null +++ b/Content.Server/Cargo/CargoOrderDatabase.cs @@ -0,0 +1,104 @@ +using Content.Shared.Prototypes.Cargo; +using System.Collections.Generic; +using System.Linq; + +namespace Content.Server.Cargo +{ + public class CargoOrderDatabase + { + private Dictionary _orders = new Dictionary(); + private int _orderNumber = 0; + + public CargoOrderDatabase(int id) + { + Id = id; + } + + public int Id { get; private set; } + + /// + /// Removes all orders from the database. + /// + public void Clear() + { + _orders.Clear(); + } + + /// + /// Returns a list of all orders. + /// + /// A list of orders + public List GetOrders() + { + return _orders.Values.ToList(); + } + + public bool TryGetOrder(int id, out CargoOrderData order) + { + if (_orders.TryGetValue(id, out var _order)) + { + order = _order; + return true; + } + order = null; + return false; + } + + public List SpliceApproved() + { + var orders = _orders.Values.Where(order => order.Approved).ToList(); + foreach (var order in orders) + _orders.Remove(order.OrderNumber); + return orders; + } + + /// + /// Adds an order to the database. + /// + /// The person who requested the item. + /// The reason the product was requested. + /// The ID of the product requested. + /// The amount of the products requested. + /// The ID of the bank account paying for the order. + /// Whether the order will be bought when the orders are processed. + public void AddOrder(string requester, string reason, string productId, int amount, int payingAccountId) + { + var order = new CargoOrderData(_orderNumber, requester, reason, productId, amount, payingAccountId); + if (Contains(order)) + return; + _orders.Add(_orderNumber, order); + _orderNumber += 1; + } + + /// + /// Removes an order from the database. + /// + /// The order to be removed. + /// Whether it could be removed or not + public bool RemoveOrder(int orderNumber) + { + return _orders.Remove(orderNumber); + } + + /// + /// Approves an order in the database. + /// + /// The order to be approved. + public void ApproveOrder(int orderNumber) + { + if (!_orders.TryGetValue(orderNumber, out var order)) + return; + order.Approved = true; + } + + /// + /// Returns whether the database contains the order or not. + /// + /// The order to check + /// Whether the database contained the order or not. + public bool Contains(CargoOrderData order) + { + return _orders.ContainsValue(order); + } + } +} diff --git a/Content.Server/Cargo/GalacticBankManager.cs b/Content.Server/Cargo/GalacticBankManager.cs new file mode 100644 index 0000000000..1fbcb81ddb --- /dev/null +++ b/Content.Server/Cargo/GalacticBankManager.cs @@ -0,0 +1,109 @@ +using Content.Server.GameObjects.Components.Cargo; +using Robust.Shared.Timing; +using System; +using System.Collections.Generic; + +namespace Content.Server.Cargo +{ + public class GalacticBankManager : IGalacticBankManager + { + private readonly float DELAY = 10f; + private readonly int POINT_INCREASE = 10; + + private int _index = 0; + private float _timer = 10f; + private readonly Dictionary _accounts = new Dictionary(); + private readonly List _components = new List(); + + public GalacticBankManager() + { + CreateBankAccount("Orbital Monitor IV Station", 100000); + } + + public IEnumerable GetAllBankAccounts() + { + return _accounts.Values; + } + + public void Shutdown() + { + throw new System.NotImplementedException(); + } + + public void CreateBankAccount(string name, int balance = 0) + { + var account = new CargoBankAccount(_index, name, balance); + _accounts.Add(_index, account); + _index += 1; + } + + public CargoBankAccount GetBankAccount(int id) + { + return _accounts[id]; + } + + public bool TryGetBankAccount(int id, out CargoBankAccount account) + { + if (_accounts.TryGetValue(id, out var _account)) + { + account = _account; + return true; + } + account = null; + return false; + } + + public void Update(FrameEventArgs frameEventArgs) + { + _timer += frameEventArgs.DeltaSeconds; + if (_timer < DELAY) + return; + _timer -= DELAY; + foreach (var account in GetAllBankAccounts()) + { + account.Balance += POINT_INCREASE; + } + SyncComponents(); + } + + private void SyncComponents() + { + foreach (var component in _components) + { + var account = GetBankAccount(component.BankId); + if (account == null) + continue; + component.SetState(account.Id, account.Name, account.Balance); + } + } + + private void SyncComponentsWithId(int id) + { + var account = GetBankAccount(id); + foreach (var component in _components) + { + if (component.BankId != id) + continue; + component.SetState(account.Id, account.Name, account.Balance); + } + } + + public void AddComponent(CargoConsoleComponent component) + { + if (_components.Contains(component)) + return; + _components.Add(component); + } + + public bool ChangeBalance(int id, int n) + { + if (!TryGetBankAccount(id, out var account)) + return false; + if (account.Balance + n < 0) + return false; + account.Balance += n; + SyncComponentsWithId(account.Id); + return true; + } + } +} diff --git a/Content.Server/Cargo/ICargoBankAccount.cs b/Content.Server/Cargo/ICargoBankAccount.cs new file mode 100644 index 0000000000..998f846c99 --- /dev/null +++ b/Content.Server/Cargo/ICargoBankAccount.cs @@ -0,0 +1,11 @@ +using System; + +namespace Content.Server.Cargo +{ + public interface ICargoBankAccount + { + int Id { get; } + string Name { get; } + int Balance { get; } + } +} diff --git a/Content.Server/Cargo/ICargoOrderDataManager.cs b/Content.Server/Cargo/ICargoOrderDataManager.cs new file mode 100644 index 0000000000..4c4f5cbbe7 --- /dev/null +++ b/Content.Server/Cargo/ICargoOrderDataManager.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; + +namespace Content.Server.Cargo +{ + public interface ICargoOrderDataManager + { + bool TryGetAccount(int id, out CargoOrderDatabase account); + void AddOrder(int id, string requester, string reason, string productId, int amount, int payingAccountId); + void RemoveOrder(int id, int orderNumber); + void ApproveOrder(int id, int orderNumber); + void AddComponent(CargoOrderDatabaseComponent component); + List GetOrdersFromAccount(int accountId); + List RemoveAndGetApprovedFrom(CargoOrderDatabase database); + } +} diff --git a/Content.Server/Cargo/IGalacticBankManager.cs b/Content.Server/Cargo/IGalacticBankManager.cs new file mode 100644 index 0000000000..a6853f04ff --- /dev/null +++ b/Content.Server/Cargo/IGalacticBankManager.cs @@ -0,0 +1,20 @@ +using Content.Server.GameObjects.Components.Cargo; +using Robust.Shared.Timing; +using System.Collections.Generic; + +namespace Content.Server.Cargo +{ + public interface IGalacticBankManager + { + IEnumerable GetAllBankAccounts(); + + void Shutdown(); + void Update(FrameEventArgs frameEventArgs); + + void CreateBankAccount(string name, int balance); + CargoBankAccount GetBankAccount(int id); + void AddComponent(CargoConsoleComponent cargoConsoleComponent); + bool TryGetBankAccount(int id, out CargoBankAccount account); + bool ChangeBalance(int id, int n); + } +} diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 46fa5e311f..adb7442ad3 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -1,4 +1,5 @@ -using Content.Server.Chat; +using Content.Server.Cargo; +using Content.Server.Chat; using Content.Server.GameTicking; using Content.Server.Interfaces; using Content.Server.Interfaces.Chat; @@ -50,6 +51,8 @@ namespace Content.Server IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); if (TestingCallbacks != null) { var cast = (ServerModuleTestingCallbacks) TestingCallbacks; @@ -83,6 +86,14 @@ namespace Content.Server base.Update(level, frameEventArgs); _gameTicker.Update(frameEventArgs); + switch (level) + { + case ModUpdateLevel.PreEngine: + { + IoCManager.Resolve().Update(frameEventArgs); + break; + } + } } } } diff --git a/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs b/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs new file mode 100644 index 0000000000..a2e21dec1a --- /dev/null +++ b/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs @@ -0,0 +1,133 @@ +using Content.Server.Cargo; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Cargo; +using Content.Shared.Prototypes.Cargo; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Server.GameObjects.Components.Cargo +{ + [RegisterComponent] + [ComponentReference(typeof(IActivate))] + public class CargoConsoleComponent : SharedCargoConsoleComponent, IActivate + { +#pragma warning disable 649 + [Dependency] private readonly IGalacticBankManager _galacticBankManager; + [Dependency] private readonly ICargoOrderDataManager _cargoOrderDataManager; +#pragma warning restore 649 + + [ViewVariables] + public int Points = 1000; + + private BoundUserInterface _userInterface; + + [ViewVariables] + public GalacticMarketComponent Market { get; private set; } + [ViewVariables] + public CargoOrderDatabaseComponent Orders { get; private set; } + [ViewVariables] + public int BankId { get; private set; } + + private bool _requestOnly = false; + + public override void Initialize() + { + base.Initialize(); + Market = Owner.GetComponent(); + Orders = Owner.GetComponent(); + _userInterface = Owner.GetComponent().GetBoundUserInterface(CargoConsoleUiKey.Key); + _userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; + _galacticBankManager.AddComponent(this); + BankId = 0; + } + + /// + /// Reads data from YAML + /// + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _requestOnly, "requestOnly", false); + } + + private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) + { + var message = serverMsg.Message; + if (!Orders.ConnectedToDatabase) + return; + switch (message) + { + case CargoConsoleAddOrderMessage msg: + { + if (msg.Amount <= 0) + break; + _cargoOrderDataManager.AddOrder(Orders.Database.Id, msg.Requester, msg.Reason, msg.ProductId, msg.Amount, BankId); + break; + } + case CargoConsoleRemoveOrderMessage msg: + { + _cargoOrderDataManager.RemoveOrder(Orders.Database.Id, msg.OrderNumber); + break; + } + case CargoConsoleApproveOrderMessage msg: + { + if (_requestOnly || + !Orders.Database.TryGetOrder(msg.OrderNumber, out var order)) + break; + _prototypeManager.TryIndex(order.ProductId, out CargoProductPrototype product); + if (product == null) + break; + if (!_galacticBankManager.ChangeBalance(BankId, (-product.PointCost) * order.Amount)) + break; + _cargoOrderDataManager.ApproveOrder(Orders.Database.Id, msg.OrderNumber); + break; + } + case CargoConsoleShuttleMessage _: + { + var approvedOrders = _cargoOrderDataManager.RemoveAndGetApprovedFrom(Orders.Database); + // TODO replace with shuttle code + + // TEMPORARY loop for spawning stuff on top of console + foreach (var order in approvedOrders) + { + if (!_prototypeManager.TryIndex(order.ProductId, out CargoProductPrototype product)) + continue; + for (var i = 0; i < order.Amount; i++) + { + Owner.EntityManager.SpawnEntityAt(product.Product, Owner.Transform.GridPosition); + } + } + break; + } + } + } + + void IActivate.Activate(ActivateEventArgs eventArgs) + { + if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) + { + return; + } + + _userInterface.Open(actor.playerSession); + } + + /// + /// Sync bank account information + /// + public void SetState(int id, string name, int balance) + { + _userInterface.SetState(new CargoConsoleInterfaceState(_requestOnly, id, name, balance)); + } + } +} diff --git a/Content.Server/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs b/Content.Server/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs new file mode 100644 index 0000000000..54013b302c --- /dev/null +++ b/Content.Server/GameObjects/Components/Cargo/CargoOrderDatabaseComponent.cs @@ -0,0 +1,30 @@ +using Content.Server.Cargo; +using Content.Shared.GameObjects.Components.Cargo; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.GameObjects.Components.Cargo +{ + [RegisterComponent] + public class CargoOrderDatabaseComponent : SharedCargoOrderDatabaseComponent + { +#pragma warning disable 649 + [Dependency] private readonly ICargoOrderDataManager _cargoOrderDataManager; +#pragma warning restore 649 + + public CargoOrderDatabase Database { get; set; } + public bool ConnectedToDatabase => Database != null; + + public override void Initialize() + { + _cargoOrderDataManager.AddComponent(this); + } + + public override ComponentState GetComponentState() + { + if (!ConnectedToDatabase) + return new CargoOrderDatabaseState(null); + return new CargoOrderDatabaseState(Database.GetOrders()); + } + } +} diff --git a/Content.Server/GameObjects/Components/Cargo/GalacticMarketComponent.cs b/Content.Server/GameObjects/Components/Cargo/GalacticMarketComponent.cs new file mode 100644 index 0000000000..5f4ff15e26 --- /dev/null +++ b/Content.Server/GameObjects/Components/Cargo/GalacticMarketComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared.GameObjects.Components.Cargo; +using Robust.Shared.GameObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Server.GameObjects.Components.Cargo +{ + [RegisterComponent] + public class GalacticMarketComponent : SharedGalacticMarketComponent + { + public override ComponentState GetComponentState() + { + return new GalacticMarketState(GetProductIdList()); + } + } +} diff --git a/Content.Shared/GameObjects/Components/Cargo/SharedCargoConsoleComponent.cs b/Content.Shared/GameObjects/Components/Cargo/SharedCargoConsoleComponent.cs new file mode 100644 index 0000000000..887c94f4be --- /dev/null +++ b/Content.Shared/GameObjects/Components/Cargo/SharedCargoConsoleComponent.cs @@ -0,0 +1,106 @@ +using Content.Shared.Prototypes.Cargo; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Shared.GameObjects.Components.Cargo +{ + public class SharedCargoConsoleComponent : Component + { +#pragma warning disable CS0649 + [Dependency] + protected IPrototypeManager _prototypeManager; +#pragma warning restore + + public sealed override string Name => "CargoConsole"; + + /// + /// Sends away or requests shuttle + /// + [Serializable, NetSerializable] + public class CargoConsoleShuttleMessage : BoundUserInterfaceMessage + { + public CargoConsoleShuttleMessage() + { + } + } + + /// + /// Add order to database. + /// + [Serializable, NetSerializable] + public class CargoConsoleAddOrderMessage : BoundUserInterfaceMessage + { + public string Requester; + public string Reason; + public string ProductId; + public int Amount; + + public CargoConsoleAddOrderMessage(string requester, string reason, string productId, int amount) + { + Requester = requester; + Reason = reason; + ProductId = productId; + Amount = amount; + } + } + + /// + /// Remove order from database. + /// + [Serializable, NetSerializable] + public class CargoConsoleRemoveOrderMessage : BoundUserInterfaceMessage + { + public int OrderNumber; + + public CargoConsoleRemoveOrderMessage(int orderNumber) + { + OrderNumber = orderNumber; + } + } + + /// + /// Set order in database as approved. + /// + [Serializable, NetSerializable] + public class CargoConsoleApproveOrderMessage : BoundUserInterfaceMessage + { + public int OrderNumber; + + public CargoConsoleApproveOrderMessage(int orderNumber) + { + OrderNumber = orderNumber; + } + } + + [NetSerializable, Serializable] + public enum CargoConsoleUiKey + { + Key + } + } + + [NetSerializable, Serializable] + public class CargoConsoleInterfaceState : BoundUserInterfaceState + { + public readonly bool RequestOnly; + public readonly int BankId; + public readonly string BankName; + public readonly int BankBalance; + + public CargoConsoleInterfaceState(bool requestOnly, int bankId, string bankName, int bankBalance) + { + RequestOnly = requestOnly; + BankId = bankId; + BankName = bankName; + BankBalance = bankBalance; + } + } +} diff --git a/Content.Shared/GameObjects/Components/Cargo/SharedCargoOrderDatabaseComponent.cs b/Content.Shared/GameObjects/Components/Cargo/SharedCargoOrderDatabaseComponent.cs new file mode 100644 index 0000000000..9a4898e888 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Cargo/SharedCargoOrderDatabaseComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared.Prototypes.Cargo; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; + +namespace Content.Shared.GameObjects.Components.Cargo +{ + public class SharedCargoOrderDatabaseComponent : Component + { + public sealed override string Name => "CargoOrderDatabase"; + public sealed override uint? NetID => ContentNetIDs.CARGO_ORDER_DATABASE; + public sealed override Type StateType => typeof(CargoOrderDatabaseState); + } + + [NetSerializable, Serializable] + public class CargoOrderDatabaseState : ComponentState + { + public readonly List Orders; + public CargoOrderDatabaseState(List orders) : base(ContentNetIDs.CARGO_ORDER_DATABASE) + { + Orders = orders; + } + } +} diff --git a/Content.Shared/GameObjects/Components/Cargo/SharedGalacticMarketComponent.cs b/Content.Shared/GameObjects/Components/Cargo/SharedGalacticMarketComponent.cs new file mode 100644 index 0000000000..f2be864dc6 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Cargo/SharedGalacticMarketComponent.cs @@ -0,0 +1,97 @@ +using Content.Shared.Prototypes.Cargo; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Content.Shared.GameObjects.Components.Cargo +{ + public class SharedGalacticMarketComponent : Component, IEnumerable + { + public sealed override string Name => "GalacticMarket"; + public sealed override uint? NetID => ContentNetIDs.GALACTIC_MARKET; + public sealed override Type StateType => typeof(GalacticMarketState); + + protected List _products = new List(); + + /// + /// A read-only list of products. + /// + public IReadOnlyList Products => _products; + + public IEnumerator GetEnumerator() + { + return _products.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns a product from the string id; + /// + /// Product + public CargoProductPrototype GetProduct(string productId) + { + var prototypeManager = IoCManager.Resolve(); + if (!prototypeManager.TryIndex(productId, out CargoProductPrototype product) || !_products.Contains(product)) + { + return null; + } + return product; + } + + /// + /// Returns a list with the IDs of all products. + /// + /// A list of product IDs + public List GetProductIdList() + { + List productIds = new List(); + + foreach (var product in _products) + { + productIds.Add(product.ID); + } + + return productIds; + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + if (serializer.Reading) + { + var products = serializer.ReadDataField("products", new List()); + var prototypeManager = IoCManager.Resolve(); + foreach (var id in products) + { + if (!prototypeManager.TryIndex(id, out CargoProductPrototype product)) + continue; + _products.Add(product); + } + } + else if (serializer.Writing) + { + var products = GetProductIdList(); + serializer.DataField(ref products, "products", new List()); + } + } + } + + [Serializable, NetSerializable] + public class GalacticMarketState : ComponentState + { + public List Products; + public GalacticMarketState(List technologies) : base(ContentNetIDs.GALACTIC_MARKET) + { + Products = technologies; + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 33f0c986e5..7fd98c5eb2 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -32,5 +32,7 @@ public const uint OVERLAYEFFECTS = 1027; public const uint STOMACH = 1028; public const uint ITEMCOOLDOWN = 1029; + public const uint CARGO_ORDER_DATABASE = 1030; + public const uint GALACTIC_MARKET = 1031; } } diff --git a/Content.Shared/Prototypes/Cargo/CargoOrderData.cs b/Content.Shared/Prototypes/Cargo/CargoOrderData.cs new file mode 100644 index 0000000000..0882db8e0d --- /dev/null +++ b/Content.Shared/Prototypes/Cargo/CargoOrderData.cs @@ -0,0 +1,34 @@ +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Shared.Prototypes.Cargo +{ + [NetSerializable, Serializable] + public class CargoOrderData + { + public int OrderNumber; + public string Requester; + // public String RequesterRank; // TODO Figure out how to get Character ID card data + // public int RequesterId; + public string Reason; + public string ProductId; + public int Amount; + public int PayingAccountId; + public bool Approved; + + public CargoOrderData(int orderNumber, string requester, string reason, string productId, int amount, int payingAccountId) + { + OrderNumber = orderNumber; + Requester = requester; + Reason = reason; + ProductId = productId; + Amount = amount; + PayingAccountId = payingAccountId; + Approved = false; + } + } +} diff --git a/Content.Shared/Prototypes/Cargo/CargoProductPrototype.cs b/Content.Shared/Prototypes/Cargo/CargoProductPrototype.cs new file mode 100644 index 0000000000..57b1aedc76 --- /dev/null +++ b/Content.Shared/Prototypes/Cargo/CargoProductPrototype.cs @@ -0,0 +1,111 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; +using System; +using YamlDotNet.RepresentationModel; + +namespace Content.Shared.Prototypes.Cargo +{ + [NetSerializable, Serializable, Prototype("cargoProduct")] + public class CargoProductPrototype : IPrototype, IIndexedPrototype + { + private string _id; + private string _name; + private string _description; + private SpriteSpecifier _icon; + private string _product; + private int _pointCost; + private string _category; + private string _group; + + [ViewVariables] + public string ID => _id; + + /// + /// Product name. + /// + [ViewVariables] + public string Name + { + get + { + if (_name.Trim().Length != 0) + return _name; + var protoMan = IoCManager.Resolve(); + if (protoMan == null) + return _name; + protoMan.TryIndex(_product, out EntityPrototype prototype); + if (prototype?.Name != null) + _name = prototype.Name; + return _name; + } + } + + /// + /// Short description of the product. + /// + [ViewVariables] + public string Description + { + get + { + if (_description.Trim().Length != 0) + return _description; + var protoMan = IoCManager.Resolve(); + if (protoMan == null) + return _description; + protoMan.TryIndex(_product, out EntityPrototype prototype); + if (prototype?.Description != null) + _description = prototype.Description; + return _description; + } + } + + /// + /// Texture path used in the CargoConsole GUI. + /// + [ViewVariables] + public SpriteSpecifier Icon => _icon; + + /// + /// The prototype name of the product. + /// + [ViewVariables] + public string Product => _product; + + /// + /// The point cost of the product. + /// + [ViewVariables] + public int PointCost => _pointCost; + + /// + /// The prototype category of the product. (e.g. Engineering, Medical) + /// + [ViewVariables] + public string Category => _category; + + /// + /// The prototype group of the product. (e.g. Contraband) + /// + [ViewVariables] + public string Group => _group; + + public void LoadFrom(YamlMappingNode mapping) + { + var serializer = YamlObjectSerializer.NewReader(mapping); + + serializer.DataField(ref _name, "name", string.Empty); + serializer.DataField(ref _id, "id", string.Empty); + serializer.DataField(ref _description, "description", string.Empty); + serializer.DataField(ref _icon, "icon", SpriteSpecifier.Invalid); + serializer.DataField(ref _product, "product", null); + serializer.DataField(ref _pointCost, "cost", 0); + serializer.DataField(ref _category, "category", string.Empty); + serializer.DataField(ref _group, "group", string.Empty); + } + } +} diff --git a/Resources/Maps/stationstation.yml b/Resources/Maps/stationstation.yml index dab579989d..62b310727a 100644 --- a/Resources/Maps/stationstation.yml +++ b/Resources/Maps/stationstation.yml @@ -1833,7 +1833,7 @@ entities: pos: -5.5,-0.5 rot: -1.5707963267949 rad type: Transform -- type: computerPowerMonitoring +- type: computerSupplyRequest uid: 153 components: - grid: 0 @@ -1846,7 +1846,7 @@ entities: mask: 19 bounds: -0.5,-0.25,0.5,0.25 type: Collidable -- type: computerAlert +- type: computerSupplyOrdering uid: 154 components: - grid: 0 diff --git a/Resources/Prototypes/Cargo/products.yml b/Resources/Prototypes/Cargo/products.yml new file mode 100644 index 0000000000..9c71c056f6 --- /dev/null +++ b/Resources/Prototypes/Cargo/products.yml @@ -0,0 +1,33 @@ +- type: cargoProduct + name: "Dice" + id: cargo.dice + description: Some dice + icon: + sprite: Objects/Fun/dice.rsi + state: d2020 + product: d20 + cost: 20 + category: Other + group: market + +- type: cargoProduct + name: "Medkit" + id: cargo.Medkit + description: Everything you need to patch someone up. + icon: Objects/Medical/medkit_r.png + product: Medkit + cost: 200 + category: Medical + group: market + +- type: cargoProduct + name: "Flashlight" + id: cargo.flashlight + description: Shine a light in the dark + icon: + sprite: Objects/Tools/flashlight.rsi + state: lantern_off + product: FlashlightLantern + cost: 3000 + category: Engineering + group: market \ No newline at end of file diff --git a/Resources/Prototypes/Entities/buildings/computers.yml b/Resources/Prototypes/Entities/buildings/computers.yml index ccf64ae12b..6a9edb3410 100644 --- a/Resources/Prototypes/Entities/buildings/computers.yml +++ b/Resources/Prototypes/Entities/buildings/computers.yml @@ -69,14 +69,38 @@ - type: entity id: computerSupplyOrdering parent: computerBase - name: Supply Ordering Computer + name: Cargo Ordering Computer components: - type: Appearance visuals: - type: ComputerVisualizer2D key: tech_key screen: supply + - type: CargoConsole + - type: CargoOrderDatabase + - type: GalacticMarket + static: true + products: + - cargo.dice + - cargo.flashlight + - cargo.Medkit + - type: UserInterface + interfaces: + - key: enum.CargoConsoleUiKey.Key + type: CargoConsoleBoundUserInterface +- type: entity + id: computerSupplyRequest + parent: computerSupplyOrdering + name: Cargo Request Computer + components: + - type: Appearance + visuals: + - type: ComputerVisualizer2D + key: tech_key + screen: request + - type: CargoConsole + requestOnly: true - type: entity id: computerMedicalRecords diff --git a/RobustToolbox b/RobustToolbox index 89f6a0917a..da440bbf28 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 89f6a0917a8109fe3d2a5b69590208c2d5696a9c +Subproject commit da440bbf28b931862c18b133daaa24634a20f784