diff --git a/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs b/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs
new file mode 100644
index 0000000000..3c7b7fe0c0
--- /dev/null
+++ b/Content.Client/Cargo/BUI/CargoPalletConsoleBoundUserInterface.cs
@@ -0,0 +1,56 @@
+using Content.Client.Cargo.UI;
+using Content.Shared.Cargo.BUI;
+using Content.Shared.Cargo.Events;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Cargo.BUI;
+
+public sealed class CargoPalletConsoleBoundUserInterface : BoundUserInterface
+{
+ private CargoPalletMenu? _menu;
+
+ public CargoPalletConsoleBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey) {}
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = new CargoPalletMenu();
+ _menu.AppraiseRequested += OnAppraisal;
+ _menu.SellRequested += OnSell;
+ _menu.OnClose += Close;
+
+ _menu.OpenCentered();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (disposing)
+ {
+ _menu?.Dispose();
+ }
+ }
+
+ private void OnAppraisal()
+ {
+ SendMessage(new CargoPalletAppraiseMessage());
+ }
+
+ private void OnSell()
+ {
+ SendMessage(new CargoPalletSellMessage());
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (state is not CargoPalletConsoleInterfaceState palletState)
+ return;
+
+ _menu?.SetEnabled(palletState.Enabled);
+ _menu?.SetAppraisal(palletState.Appraisal);
+ _menu?.SetCount(palletState.Count);
+ }
+}
diff --git a/Content.Client/Cargo/UI/CargoPalletMenu.xaml b/Content.Client/Cargo/UI/CargoPalletMenu.xaml
new file mode 100644
index 0000000000..489c6cb8f6
--- /dev/null
+++ b/Content.Client/Cargo/UI/CargoPalletMenu.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Cargo/UI/CargoPalletMenu.xaml.cs b/Content.Client/Cargo/UI/CargoPalletMenu.xaml.cs
new file mode 100644
index 0000000000..c46ecf562d
--- /dev/null
+++ b/Content.Client/Cargo/UI/CargoPalletMenu.xaml.cs
@@ -0,0 +1,47 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Cargo;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Cargo.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class CargoPalletMenu : FancyWindow
+{
+ public Action? SellRequested;
+ public Action? AppraiseRequested;
+
+ public CargoPalletMenu()
+ {
+ RobustXamlLoader.Load(this);
+ SellButton.OnPressed += OnSellPressed;
+ AppraiseButton.OnPressed += OnAppraisePressed;
+ Title = Loc.GetString("cargo-pallet-console-menu-title");
+ }
+
+ public void SetAppraisal(int amount)
+ {
+ AppraisalLabel.Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", amount.ToString()));
+ }
+
+ public void SetCount(int count)
+ {
+ CountLabel.Text = count.ToString();
+ }
+ public void SetEnabled(bool enabled)
+ {
+ AppraiseButton.Disabled = !enabled;
+ SellButton.Disabled = !enabled;
+ }
+
+ private void OnSellPressed(BaseButton.ButtonEventArgs obj)
+ {
+ SellRequested?.Invoke();
+ }
+
+ private void OnAppraisePressed(BaseButton.ButtonEventArgs obj)
+ {
+ AppraiseRequested?.Invoke();
+ }
+}
diff --git a/Content.Server/Cargo/Components/CargoPalletConsoleComponent.cs b/Content.Server/Cargo/Components/CargoPalletConsoleComponent.cs
new file mode 100644
index 0000000000..86102bb846
--- /dev/null
+++ b/Content.Server/Cargo/Components/CargoPalletConsoleComponent.cs
@@ -0,0 +1,13 @@
+using Content.Server.Cargo.Systems;
+using Content.Shared.Stacks;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.Cargo.Components;
+
+[RegisterComponent]
+[Access(typeof(CargoSystem))]
+public sealed class CargoPalletConsoleComponent : Component
+{
+ [ViewVariables(VVAccess.ReadWrite), DataField("cashType", customTypeSerializer:typeof(PrototypeIdSerializer))]
+ public string CashType = "Credit";
+}
diff --git a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs
index fbf7bda83b..3bd90aeb3e 100644
--- a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs
+++ b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs
@@ -7,6 +7,8 @@ using Content.Server.UserInterface;
using Content.Server.Paper;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Components;
+using Content.Server.Stack;
+using Content.Shared.Stacks;
using Content.Shared.Cargo;
using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Components;
@@ -27,6 +29,8 @@ using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
+using Robust.Shared.Prototypes;
+using Content.Shared.Coordinates;
namespace Content.Server.Cargo.Systems;
@@ -40,13 +44,14 @@ public sealed partial class CargoSystem
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
-
+ [Dependency] private readonly StackSystem _stack = default!;
public MapId? CargoMap { get; private set; }
private const float CallOffset = 50f;
@@ -69,6 +74,10 @@ public sealed partial class CargoSystem
SubscribeLocalEvent(OnCargoShuttleCall);
SubscribeLocalEvent(RecallCargoShuttle);
+ SubscribeLocalEvent(OnPalletSale);
+ SubscribeLocalEvent(OnPalletAppraise);
+ SubscribeLocalEvent(OnPalletUIOpen);
+
SubscribeLocalEvent(OnCargoGetConsole);
SubscribeLocalEvent(OnCargoPilotConsoleOpen);
SubscribeLocalEvent(OnCargoPilotConsoleClose);
@@ -144,6 +153,48 @@ public sealed partial class CargoSystem
}
}
+ private void UpdatePalletConsoleInterface(EntityUid uid, CargoPalletConsoleComponent component)
+ {
+ var bui = _uiSystem.GetUi(component.Owner, CargoPalletConsoleUiKey.Sale);
+ if (Transform(uid).GridUid is not EntityUid gridUid)
+ {
+ _uiSystem.SetUiState(bui,
+ new CargoPalletConsoleInterfaceState(0, 0, false));
+ return;
+ }
+ GetPalletGoods(gridUid, out var toSell, out var amount);
+ _uiSystem.SetUiState(bui,
+ new CargoPalletConsoleInterfaceState((int) amount, toSell.Count, true));
+ }
+
+ private void OnPalletUIOpen(EntityUid uid, CargoPalletConsoleComponent component, BoundUIOpenedEvent args)
+ {
+ var player = args.Session.AttachedEntity;
+
+ if (player == null)
+ return;
+
+ UpdatePalletConsoleInterface(uid, component);
+ }
+
+ ///
+ /// Ok so this is just the same thing as opening the UI, its a refresh button.
+ /// I know this would probably feel better if it were like predicted and dynamic as pallet contents change
+ /// However.
+ /// I dont want it to explode if cargo uses a conveyor to move 8000 pineapple slices or whatever, they are
+ /// known for their entity spam i wouldnt put it past them
+ ///
+
+ private void OnPalletAppraise(EntityUid uid, CargoPalletConsoleComponent component, CargoPalletAppraiseMessage args)
+ {
+ var player = args.Session.AttachedEntity;
+
+ if (player == null)
+ return;
+
+ UpdatePalletConsoleInterface(uid, component);
+ }
+
private void OnCargoShuttleConsoleStartup(EntityUid uid, CargoShuttleConsoleComponent component, ComponentStartup args)
{
var station = _station.GetOwningStation(uid);
@@ -209,7 +260,7 @@ public sealed partial class CargoSystem
if (component == null || shuttle == null || component.Orders.Count == 0)
return orders;
- var spaceRemaining = GetCargoSpace(shuttle);
+ var spaceRemaining = GetCargoSpace(shuttle.Owner);
for( var i = 0; i < component.Orders.Count && spaceRemaining > 0; i++)
{
var order = component.Orders[i];
@@ -238,19 +289,19 @@ public sealed partial class CargoSystem
///
/// Get the amount of space the cargo shuttle can fit for orders.
///
- private int GetCargoSpace(CargoShuttleComponent component)
+ private int GetCargoSpace(EntityUid gridUid)
{
- var space = GetCargoPallets(component).Count;
+ var space = GetCargoPallets(gridUid).Count;
return space;
}
- private List GetCargoPallets(CargoShuttleComponent component)
+ private List GetCargoPallets(EntityUid gridUid)
{
var pads = new List();
foreach (var (comp, compXform) in EntityQuery(true))
{
- if (compXform.ParentUid != component.Owner ||
+ if (compXform.ParentUid != gridUid ||
!compXform.Anchored) continue;
pads.Add(comp);
@@ -306,14 +357,25 @@ public sealed partial class CargoSystem
_sawmill.Info($"Added cargo shuttle to {ToPrettyString(shuttleUid)}");
}
- private void SellPallets(CargoShuttleComponent component, StationBankAccountComponent bank)
+ private void SellPallets(EntityUid gridUid, out double amount)
{
- double amount = 0;
- var toSell = new HashSet();
+ GetPalletGoods(gridUid, out var toSell, out amount);
+
+ _sawmill.Debug($"Cargo sold {toSell.Count} entities for {amount}");
+
+ foreach (var ent in toSell)
+ {
+ Del(ent);
+ }
+ }
+
+ private void GetPalletGoods(EntityUid gridUid, out HashSet toSell, out double amount)
+ {
+ amount = 0;
var xformQuery = GetEntityQuery();
var blacklistQuery = GetEntityQuery();
-
- foreach (var pallet in GetCargoPallets(component))
+ toSell = new HashSet();
+ foreach (var pallet in GetCargoPallets(gridUid))
{
// Containers should already get the sell price of their children so can skip those.
foreach (var ent in _lookup.GetEntitiesIntersecting(pallet.Owner, LookupFlags.Dynamic | LookupFlags.Sundries | LookupFlags.Approximate))
@@ -336,14 +398,6 @@ public sealed partial class CargoSystem
amount += price;
}
}
-
- bank.Balance += (int) amount;
- _sawmill.Debug($"Cargo sold {toSell.Count} entities for {amount}");
-
- foreach (var ent in toSell)
- {
- Del(ent);
- }
}
private void SendToCargoMap(EntityUid uid, CargoShuttleComponent? component = null)
@@ -424,7 +478,7 @@ public sealed partial class CargoSystem
{
var xformQuery = GetEntityQuery();
- var pads = GetCargoPallets(shuttle);
+ var pads = GetCargoPallets(shuttle.Owner);
while (pads.Count > 0)
{
var coordinates = new EntityCoordinates(shuttle.Owner, xformQuery.GetComponent(_random.PickAndTake(pads).Owner).LocalPosition);
@@ -435,13 +489,34 @@ public sealed partial class CargoSystem
}
}
+ private void OnPalletSale(EntityUid uid, CargoPalletConsoleComponent component, CargoPalletSellMessage args)
+ {
+ var player = args.Session.AttachedEntity;
+
+ if (player == null)
+ return;
+
+ var bui = _uiSystem.GetUi(component.Owner, CargoPalletConsoleUiKey.Sale);
+ if (Transform(uid).GridUid is not EntityUid gridUid)
+ {
+ _uiSystem.SetUiState(bui,
+ new CargoPalletConsoleInterfaceState(0, 0, false));
+ return;
+ }
+
+ SellPallets(gridUid, out var price);
+ var stackPrototype = _prototypeManager.Index(component.CashType);
+ _stack.Spawn((int)price, stackPrototype, uid.ToCoordinates());
+ UpdatePalletConsoleInterface(uid, component);
+ }
+
private void RecallCargoShuttle(EntityUid uid, CargoShuttleConsoleComponent component, CargoRecallShuttleMessage args)
{
var player = args.Session.AttachedEntity;
if (player == null) return;
- var stationUid = _station.GetOwningStation(component.Owner);
+ var stationUid = _station.GetOwningStation(uid);
if (!TryComp(stationUid, out var orderDatabase) ||
!TryComp(stationUid, out var bank)) return;
@@ -465,7 +540,8 @@ public sealed partial class CargoSystem
return;
}
- SellPallets(shuttle, bank);
+ SellPallets((EntityUid) orderDatabase.Shuttle, out double price);
+ bank.Balance += (int) price;
_console.RefreshShuttleConsoles();
SendToCargoMap(orderDatabase.Shuttle.Value);
}
diff --git a/Content.Shared/Cargo/BUI/CargoPalletConsoleInterfaceState.cs b/Content.Shared/Cargo/BUI/CargoPalletConsoleInterfaceState.cs
new file mode 100644
index 0000000000..810801c804
--- /dev/null
+++ b/Content.Shared/Cargo/BUI/CargoPalletConsoleInterfaceState.cs
@@ -0,0 +1,29 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Cargo.BUI;
+
+[NetSerializable, Serializable]
+public sealed class CargoPalletConsoleInterfaceState : BoundUserInterfaceState
+{
+ ///
+ /// estimated apraised value of all the entities on top of pallets on the same grid as the console
+ ///
+ public int Appraisal;
+
+ ///
+ /// number of entities on top of pallets on the same grid as the console
+ ///
+ public int Count;
+
+ ///
+ /// are the buttons enabled
+ ///
+ public bool Enabled;
+
+ public CargoPalletConsoleInterfaceState(int appraisal, int count, bool enabled)
+ {
+ Appraisal = appraisal;
+ Count = count;
+ Enabled = enabled;
+ }
+}
diff --git a/Content.Shared/Cargo/Events/CargoPalletAppraiseMessage.cs b/Content.Shared/Cargo/Events/CargoPalletAppraiseMessage.cs
new file mode 100644
index 0000000000..a5e245575c
--- /dev/null
+++ b/Content.Shared/Cargo/Events/CargoPalletAppraiseMessage.cs
@@ -0,0 +1,12 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Cargo.Events;
+
+///
+/// Raised on a client request to refresh the pallet console
+///
+[Serializable, NetSerializable]
+public sealed class CargoPalletAppraiseMessage : BoundUserInterfaceMessage
+{
+
+}
diff --git a/Content.Shared/Cargo/Events/CargoPalletSellMessage.cs b/Content.Shared/Cargo/Events/CargoPalletSellMessage.cs
new file mode 100644
index 0000000000..ffb759546a
--- /dev/null
+++ b/Content.Shared/Cargo/Events/CargoPalletSellMessage.cs
@@ -0,0 +1,12 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Cargo.Events;
+
+///
+/// Raised on a client request pallet sale
+///
+[Serializable, NetSerializable]
+public sealed class CargoPalletSellMessage : BoundUserInterfaceMessage
+{
+
+}
diff --git a/Content.Shared/Cargo/SharedCargoSystem.cs b/Content.Shared/Cargo/SharedCargoSystem.cs
index 2e2546dadb..98ea3bc00c 100644
--- a/Content.Shared/Cargo/SharedCargoSystem.cs
+++ b/Content.Shared/Cargo/SharedCargoSystem.cs
@@ -10,6 +10,12 @@ public enum CargoConsoleUiKey : byte
Telepad
}
+[NetSerializable, Serializable]
+public enum CargoPalletConsoleUiKey : byte
+{
+ Sale
+}
+
public abstract class SharedCargoSystem : EntitySystem {}
[Serializable, NetSerializable]
diff --git a/Resources/Locale/en-US/cargo/cargo-pallet-console-component.ftl b/Resources/Locale/en-US/cargo/cargo-pallet-console-component.ftl
new file mode 100644
index 0000000000..b9fbd0f057
--- /dev/null
+++ b/Resources/Locale/en-US/cargo/cargo-pallet-console-component.ftl
@@ -0,0 +1,7 @@
+
+# Cargo pallet sale console
+cargo-pallet-console-menu-title = Cargo sale console
+cargo-pallet-menu-appraisal-label = Estimated Value:{" "}
+cargo-pallet-menu-count-label = Number of sale items:{" "}
+cargo-pallet-appraise-button = Appraise
+cargo-pallet-sell-button = Sell
diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
index 418858aa83..4d586521da 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
@@ -791,3 +791,33 @@
interfaces:
- key: enum.SurveillanceCameraMonitorUiKey.Key
type: SurveillanceCameraMonitorBoundUserInterface
+
+- type: entity
+ id: ComputerPalletConsole
+ parent: BaseComputer
+ name: cargo sale computer
+ description: Used to sell goods loaded onto cargo pallets
+ components:
+ - type: Sprite
+ layers:
+ - map: ["computerLayerBody"]
+ state: computer
+ - map: ["computerLayerKeyboard"]
+ state: generic_keyboard
+ - map: ["computerLayerScreen"]
+ state: request
+ - map: ["computerLayerKeys"]
+ state: tech_key
+ - type: CargoPalletConsole
+ - type: ActivatableUI
+ key: enum.CargoPalletConsoleUiKey.Sale
+ - type: UserInterface
+ interfaces:
+ - key: enum.CargoPalletConsoleUiKey.Sale
+ type: CargoPalletConsoleBoundUserInterface
+ - type: Computer
+ board: CargoRequestComputerCircuitboard
+ - type: PointLight
+ radius: 1.5
+ energy: 1.6
+ color: "#b89f25"
\ No newline at end of file