diff --git a/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs index 6c9af85a2c..ce12d16cb5 100644 --- a/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs +++ b/Content.Client/Cargo/BUI/CargoOrderConsoleBoundUserInterface.cs @@ -50,7 +50,7 @@ namespace Content.Client.Cargo.BUI base.Open(); var spriteSystem = EntMan.System(); - _menu = new CargoConsoleMenu(IoCManager.Resolve(), spriteSystem); + _menu = new CargoConsoleMenu(Owner, IoCManager.Resolve(), IoCManager.Resolve(), spriteSystem); var localPlayer = IoCManager.Resolve()?.LocalPlayer?.ControlledEntity; var description = new FormattedMessage(); diff --git a/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs b/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs index 0cc17a6246..5402a24667 100644 --- a/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs +++ b/Content.Client/Cargo/UI/CargoConsoleMenu.xaml.cs @@ -1,6 +1,7 @@ using System.Linq; 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; @@ -14,8 +15,10 @@ namespace Content.Client.Cargo.UI [GenerateTypedNameReferences] public sealed partial class CargoConsoleMenu : FancyWindow { + private IEntityManager _entityManager; private IPrototypeManager _protoManager; private SpriteSystem _spriteSystem; + private EntityUid _owner; public event Action? OnItemSelected; public event Action? OnOrderApproved; @@ -24,11 +27,13 @@ namespace Content.Client.Cargo.UI private readonly List _categoryStrings = new(); private string? _category; - public CargoConsoleMenu(IPrototypeManager protoManager, SpriteSystem spriteSystem) + public CargoConsoleMenu(EntityUid owner, IEntityManager entMan, IPrototypeManager protoManager, SpriteSystem spriteSystem) { RobustXamlLoader.Load(this); + _entityManager = entMan; _protoManager = protoManager; _spriteSystem = spriteSystem; + _owner = owner; Title = Loc.GetString("cargo-console-menu-title"); @@ -53,7 +58,21 @@ namespace Content.Client.Cargo.UI Categories.SelectId(id); } - public IEnumerable ProductPrototypes => _protoManager.EnumeratePrototypes(); + public IEnumerable ProductPrototypes + { + get + { + var allowedGroups = _entityManager.GetComponentOrNull(_owner)?.AllowedGroups; + + foreach (var cargoPrototype in _protoManager.EnumeratePrototypes()) + { + if (!allowedGroups?.Contains(cargoPrototype.Group) ?? false) + continue; + + yield return cargoPrototype; + } + } + } /// /// Populates the list of products that will actually be shown, using the current filters. @@ -80,7 +99,7 @@ namespace Content.Client.Cargo.UI Product = prototype, ProductName = { Text = prototype.Name }, MainButton = { ToolTip = prototype.Description }, - PointCost = { Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", prototype.PointCost.ToString())) }, + PointCost = { Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", prototype.Cost.ToString())) }, Icon = { Texture = _spriteSystem.Frame0(prototype.Icon) }, }; button.MainButton.OnPressed += args => diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index 8bb07cfd96..e49f2e996b 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -44,7 +44,7 @@ public sealed class CargoTest var ent = entManager.SpawnEntity(proto.Product, testMap.MapCoords); var price = pricing.GetPrice(ent); - Assert.That(price, Is.AtMost(proto.PointCost), $"Found arbitrage on {proto.ID} cargo product! Cost is {proto.PointCost} but sell is {price}!"); + Assert.That(price, Is.AtMost(proto.Cost), $"Found arbitrage on {proto.ID} cargo product! Cost is {proto.Cost} but sell is {price}!"); entManager.DeleteEntity(ent); } }); @@ -80,7 +80,7 @@ public sealed class CargoTest foreach (var bounty in bounties) { if (cargo.IsBountyComplete(ent, bounty)) - Assert.That(proto.PointCost, Is.GreaterThanOrEqualTo(bounty.Reward), $"Found arbitrage on {bounty.ID} cargo bounty! Product {proto.ID} costs {proto.PointCost} but fulfills bounty {bounty.ID} with reward {bounty.Reward}!"); + Assert.That(proto.Cost, Is.GreaterThanOrEqualTo(bounty.Reward), $"Found arbitrage on {bounty.ID} cargo bounty! Product {proto.ID} costs {proto.Cost} but fulfills bounty {bounty.ID} with reward {bounty.Reward}!"); } entManager.DeleteEntity(ent); diff --git a/Content.Server/Cargo/Components/CargoOrderConsoleComponent.cs b/Content.Server/Cargo/Components/CargoOrderConsoleComponent.cs deleted file mode 100644 index 1c418c9cc7..0000000000 --- a/Content.Server/Cargo/Components/CargoOrderConsoleComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Robust.Shared.Audio; - -namespace Content.Server.Cargo.Components -{ - /// - /// Handles sending order requests to cargo. Doesn't handle orders themselves via shuttle or telepads. - /// - [RegisterComponent] - public sealed partial class CargoOrderConsoleComponent : Component - { - [DataField("soundError")] public SoundSpecifier ErrorSound = - new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg"); - - [DataField("soundConfirm")] - public SoundSpecifier ConfirmSound = new SoundPathSpecifier("/Audio/Effects/Cargo/ping.ogg"); - } -} diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index 9a547bb578..5985bbb484 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -261,6 +261,9 @@ namespace Content.Server.Cargo.Systems return; } + if (!component.AllowedGroups.Contains(product.Group)) + return; + var data = GetOrderData(args, product, GenerateOrderId(orderDatabase)); if (!TryAddOrder(stationUid.Value, data, orderDatabase)) @@ -313,7 +316,7 @@ namespace Content.Server.Cargo.Systems private static CargoOrderData GetOrderData(CargoConsoleAddOrderMessage args, CargoProductPrototype cargoProduct, int id) { - return new CargoOrderData(id, cargoProduct.Product, cargoProduct.PointCost, args.Amount, args.Requester, args.Reason); + return new CargoOrderData(id, cargoProduct.Product, cargoProduct.Cost, args.Amount, args.Requester, args.Reason); } public static int GetOutstandingOrderCount(StationCargoOrderDatabaseComponent component) diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index c39ffb186a..d4be68efc8 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -9,6 +9,7 @@ using Content.Server.Station.Systems; using Content.Shared.Access.Systems; using Content.Shared.Administration.Logs; using Content.Shared.Cargo; +using Content.Shared.Cargo.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Mobs.Components; using JetBrains.Annotations; diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index f0f9586ad3..4d3ffa005d 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -60,7 +60,7 @@ public sealed class CargoGiftsRule : StationEventSystem if (!_cargoSystem.AddAndApproveOrder( station!.Value, product.Product, - product.PointCost, + product.Cost, qty, Loc.GetString(component.Sender), Loc.GetString(component.Description), diff --git a/Content.Shared/Cargo/Components/CargoOrderConsoleComponent.cs b/Content.Shared/Cargo/Components/CargoOrderConsoleComponent.cs new file mode 100644 index 0000000000..a7d1f53175 --- /dev/null +++ b/Content.Shared/Cargo/Components/CargoOrderConsoleComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared.Cargo.Prototypes; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Cargo.Components; + +/// +/// Handles sending order requests to cargo. Doesn't handle orders themselves via shuttle or telepads. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class CargoOrderConsoleComponent : Component +{ + [DataField("soundError")] public SoundSpecifier ErrorSound = + new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg"); + + [DataField("soundConfirm")] + public SoundSpecifier ConfirmSound = new SoundPathSpecifier("/Audio/Effects/Cargo/ping.ogg"); + + /// + /// All of the s that are supported. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public List AllowedGroups = new() { "market" }; +} + diff --git a/Content.Shared/Cargo/Prototypes/CargoProductPrototype.cs b/Content.Shared/Cargo/Prototypes/CargoProductPrototype.cs index 5487618309..1d0ca8abdb 100644 --- a/Content.Shared/Cargo/Prototypes/CargoProductPrototype.cs +++ b/Content.Shared/Cargo/Prototypes/CargoProductPrototype.cs @@ -1,12 +1,22 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; using Robust.Shared.Utility; namespace Content.Shared.Cargo.Prototypes { - [Prototype("cargoProduct")] - public sealed partial class CargoProductPrototype : IPrototype + [Prototype] + public sealed partial class CargoProductPrototype : IPrototype, IInheritingPrototype { + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; } + [DataField("name")] private string _name = string.Empty; [DataField("description")] private string _description = string.Empty; @@ -58,31 +68,31 @@ namespace Content.Shared.Cargo.Prototypes /// /// Texture path used in the CargoConsole GUI. /// - [DataField("icon")] + [DataField] public SpriteSpecifier Icon { get; private set; } = SpriteSpecifier.Invalid; /// - /// The prototype name of the product. + /// The entity prototype ID of the product. /// - [DataField("product", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Product { get; private set; } = string.Empty; + [DataField] + public EntProtoId Product { get; private set; } = string.Empty; /// /// The point cost of the product. /// - [DataField("cost")] - public int PointCost { get; private set; } + [DataField] + public int Cost { get; private set; } /// /// The prototype category of the product. (e.g. Engineering, Medical) /// - [DataField("category")] + [DataField] public string Category { get; private set; } = string.Empty; /// /// The prototype group of the product. (e.g. Contraband) /// - [DataField("group")] - public string Group { get; private set; } = string.Empty; + [DataField] + public string Group { get; private set; } = "market"; } }