2023-03-05 04:27:30 +00:00
using System.Diagnostics.CodeAnalysis ;
2022-06-23 14:36:47 +10:00
using Content.Server.Cargo.Components ;
2024-01-19 13:02:28 +11:00
using Content.Server.Station.Components ;
2022-06-23 14:36:47 +10:00
using Content.Shared.Cargo ;
using Content.Shared.Cargo.BUI ;
2024-01-19 18:23:07 +11:00
using Content.Shared.Cargo.Components ;
2022-06-23 14:36:47 +10:00
using Content.Shared.Cargo.Events ;
using Content.Shared.Cargo.Prototypes ;
2022-12-15 12:33:27 -06:00
using Content.Shared.Database ;
2025-01-30 05:05:47 +01:00
using Content.Shared.Emag.Systems ;
2024-09-03 16:01:38 +03:00
using Content.Shared.IdentityManagement ;
2024-01-19 18:23:07 +11:00
using Content.Shared.Interaction ;
2025-04-11 01:19:48 -04:00
using Content.Shared.Labels.Components ;
2024-08-04 21:23:23 -07:00
using Content.Shared.Paper ;
2025-04-13 09:22:36 -04:00
using JetBrains.Annotations ;
2025-05-06 15:04:18 -04:00
using Robust.Shared.Audio ;
2023-03-05 04:27:30 +00:00
using Robust.Shared.Map ;
2023-06-16 05:19:02 +00:00
using Robust.Shared.Prototypes ;
2025-05-07 20:34:44 -04:00
using Robust.Shared.Timing ;
2023-06-16 05:19:02 +00:00
using Robust.Shared.Utility ;
2022-06-23 14:36:47 +10:00
namespace Content.Server.Cargo.Systems
{
public sealed partial class CargoSystem
{
2024-05-25 21:26:48 +01:00
[Dependency] private readonly SharedTransformSystem _transformSystem = default ! ;
2025-01-30 05:05:47 +01:00
[Dependency] private readonly EmagSystem _emag = default ! ;
2025-05-07 20:34:44 -04:00
[Dependency] private readonly IGameTiming _timing = default ! ;
2024-05-25 21:26:48 +01:00
2022-06-23 14:36:47 +10:00
private void InitializeConsole ( )
{
SubscribeLocalEvent < CargoOrderConsoleComponent , CargoConsoleAddOrderMessage > ( OnAddOrderMessage ) ;
SubscribeLocalEvent < CargoOrderConsoleComponent , CargoConsoleRemoveOrderMessage > ( OnRemoveOrderMessage ) ;
SubscribeLocalEvent < CargoOrderConsoleComponent , CargoConsoleApproveOrderMessage > ( OnApproveOrderMessage ) ;
SubscribeLocalEvent < CargoOrderConsoleComponent , BoundUIOpenedEvent > ( OnOrderUIOpened ) ;
SubscribeLocalEvent < CargoOrderConsoleComponent , ComponentInit > ( OnInit ) ;
2024-01-19 18:23:07 +11:00
SubscribeLocalEvent < CargoOrderConsoleComponent , InteractUsingEvent > ( OnInteractUsing ) ;
2025-01-30 05:05:47 +01:00
SubscribeLocalEvent < CargoOrderConsoleComponent , GotEmaggedEvent > ( OnEmagged ) ;
2022-06-23 14:36:47 +10:00
}
2025-05-06 15:04:18 -04:00
private void OnInteractUsingCash ( EntityUid uid , CargoOrderConsoleComponent component , ref InteractUsingEvent args )
2024-01-19 18:23:07 +11:00
{
var price = _pricing . GetPrice ( args . Used ) ;
if ( price = = 0 )
return ;
var stationUid = _station . GetOwningStation ( args . Used ) ;
if ( ! TryComp ( stationUid , out StationBankAccountComponent ? bank ) )
return ;
2025-04-13 09:22:36 -04:00
_audio . PlayPvs ( ApproveSound , uid ) ;
2025-04-17 22:06:29 -04:00
UpdateBankAccount ( ( stationUid . Value , bank ) , ( int ) price , component . Account ) ;
2024-01-19 18:23:07 +11:00
QueueDel ( args . Used ) ;
2025-02-02 11:39:23 -08:00
args . Handled = true ;
2024-01-19 18:23:07 +11:00
}
2025-05-06 15:04:18 -04:00
private void OnInteractUsingSlip ( Entity < CargoOrderConsoleComponent > ent , ref InteractUsingEvent args , CargoSlipComponent slip )
{
if ( slip . OrderQuantity < = 0 )
return ;
var stationUid = _station . GetOwningStation ( ent ) ;
if ( ! TryGetOrderDatabase ( stationUid , out var orderDatabase ) )
return ;
if ( ! _protoMan . TryIndex ( slip . Product , out var product ) )
{
Log . Error ( $"Tried to add invalid cargo product {slip.Product} as order!" ) ;
return ;
}
if ( ! ent . Comp . AllowedGroups . Contains ( product . Group ) )
return ;
var orderId = GenerateOrderId ( orderDatabase ) ;
var data = new CargoOrderData ( orderId , product . Product , product . Name , product . Cost , slip . OrderQuantity , slip . Requester , slip . Reason , slip . Account ) ;
if ( ! TryAddOrder ( stationUid . Value , ent . Comp . Account , data , orderDatabase ) )
{
PlayDenySound ( ent , ent . Comp ) ;
return ;
}
// Log order addition
_audio . PlayPvs ( ent . Comp . ScanSound , ent ) ;
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
$"{ToPrettyString(args.User):user} inserted order slip [orderId:{data.OrderId}, quantity:{data.OrderQuantity}, product:{data.ProductId}, requester:{data.Requester}, reason:{data.Reason}]" ) ;
QueueDel ( args . Used ) ;
args . Handled = true ;
}
private void OnInteractUsing ( EntityUid uid , CargoOrderConsoleComponent component , ref InteractUsingEvent args )
{
if ( HasComp < CashComponent > ( args . Used ) )
{
OnInteractUsingCash ( uid , component , ref args ) ;
}
else if ( TryComp < CargoSlipComponent > ( args . Used , out var slip ) & & ! component . SlipPrinter )
{
OnInteractUsingSlip ( ( uid , component ) , ref args , slip ) ;
}
}
2022-06-23 14:36:47 +10:00
private void OnInit ( EntityUid uid , CargoOrderConsoleComponent orderConsole , ComponentInit args )
{
var station = _station . GetOwningStation ( uid ) ;
2023-07-08 09:02:17 -07:00
UpdateOrderState ( uid , station ) ;
2022-06-23 14:36:47 +10:00
}
2025-01-30 05:05:47 +01:00
private void OnEmagged ( Entity < CargoOrderConsoleComponent > ent , ref GotEmaggedEvent args )
{
if ( ! _emag . CompareFlag ( args . Type , EmagType . Interaction ) )
return ;
if ( _emag . CheckFlag ( ent , EmagType . Interaction ) )
return ;
args . Handled = true ;
}
2025-04-13 09:22:36 -04:00
private void UpdateConsole ( )
2022-06-23 14:36:47 +10:00
{
2025-04-13 09:22:36 -04:00
var stationQuery = EntityQueryEnumerator < StationBankAccountComponent > ( ) ;
while ( stationQuery . MoveNext ( out var uid , out var bank ) )
2022-06-23 14:36:47 +10:00
{
2025-04-14 19:00:34 -04:00
if ( Timing . CurTime < bank . NextIncomeTime )
2025-04-13 09:22:36 -04:00
continue ;
bank . NextIncomeTime + = bank . IncomeDelay ;
2022-06-23 14:36:47 +10:00
2025-04-13 09:22:36 -04:00
var balanceToAdd = ( int ) Math . Round ( bank . IncreasePerSecond * bank . IncomeDelay . TotalSeconds ) ;
UpdateBankAccount ( ( uid , bank ) , balanceToAdd , bank . RevenueDistribution ) ;
2022-06-23 14:36:47 +10:00
}
}
#region Interface
private void OnApproveOrderMessage ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleApproveOrderMessage args )
{
2024-04-26 18:16:24 +10:00
if ( args . Actor is not { Valid : true } player )
2022-06-23 14:36:47 +10:00
return ;
2025-05-06 15:04:18 -04:00
if ( component . SlipPrinter )
return ;
2022-06-23 14:36:47 +10:00
if ( ! _accessReaderSystem . IsAllowed ( player , uid ) )
{
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-order-not-allowed" ) ) ;
2022-06-23 14:36:47 +10:00
PlayDenySound ( uid , component ) ;
return ;
}
2024-01-19 13:02:28 +11:00
var station = _station . GetOwningStation ( uid ) ;
2022-06-23 14:36:47 +10:00
// No station to deduct from.
2024-01-19 13:02:28 +11:00
if ( ! TryComp ( station , out StationBankAccountComponent ? bank ) | |
! TryComp ( station , out StationDataComponent ? stationData ) | |
! TryGetOrderDatabase ( station , out var orderDatabase ) )
2022-06-23 14:36:47 +10:00
{
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-station-not-found" ) ) ;
2022-06-23 14:36:47 +10:00
PlayDenySound ( uid , component ) ;
return ;
}
2023-03-05 04:27:30 +00:00
// Find our order again. It might have been dispatched or approved already
2025-04-13 09:22:36 -04:00
var order = orderDatabase . Orders [ component . Account ] . Find ( order = > args . OrderId = = order . OrderId & & ! order . Approved ) ;
2025-05-06 15:04:18 -04:00
if ( order = = null | | ! _protoMan . TryIndex ( order . Account , out var account ) )
2023-03-05 04:27:30 +00:00
{
return ;
}
2022-06-23 14:36:47 +10:00
// Invalid order
2023-06-16 05:19:02 +00:00
if ( ! _protoMan . HasIndex < EntityPrototype > ( order . ProductId ) )
2022-06-23 14:36:47 +10:00
{
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-invalid-product" ) ) ;
2022-06-23 14:36:47 +10:00
PlayDenySound ( uid , component ) ;
return ;
}
2025-05-06 15:04:18 -04:00
var amount = GetOutstandingOrderCount ( orderDatabase , order . Account ) ;
2022-06-23 14:36:47 +10:00
var capacity = orderDatabase . Capacity ;
// Too many orders, avoid them getting spammed in the UI.
if ( amount > = capacity )
{
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-too-many" ) ) ;
2022-06-23 14:36:47 +10:00
PlayDenySound ( uid , component ) ;
return ;
}
// Cap orders so someone can't spam thousands.
2023-03-05 04:27:30 +00:00
var cappedAmount = Math . Min ( capacity - amount , order . OrderQuantity ) ;
2022-06-23 14:36:47 +10:00
2023-03-05 04:27:30 +00:00
if ( cappedAmount ! = order . OrderQuantity )
2022-06-23 14:36:47 +10:00
{
2023-03-05 04:27:30 +00:00
order . OrderQuantity = cappedAmount ;
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-snip-snip" ) ) ;
2022-06-23 14:36:47 +10:00
PlayDenySound ( uid , component ) ;
}
2023-06-16 05:19:02 +00:00
var cost = order . Price * order . OrderQuantity ;
2025-05-06 15:04:18 -04:00
var accountBalance = GetBalanceFromAccount ( ( station . Value , bank ) , order . Account ) ;
2022-06-23 14:36:47 +10:00
// Not enough balance
2025-04-13 09:22:36 -04:00
if ( cost > accountBalance )
2022-06-23 14:36:47 +10:00
{
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-insufficient-funds" , ( "cost" , cost ) ) ) ;
2022-06-23 14:36:47 +10:00
PlayDenySound ( uid , component ) ;
return ;
}
2024-04-23 08:07:12 -04:00
var ev = new FulfillCargoOrderEvent ( ( station . Value , stationData ) , order , ( uid , component ) ) ;
RaiseLocalEvent ( ref ev ) ;
ev . FulfillmentEntity ? ? = station . Value ;
2024-01-30 06:05:20 -05:00
2024-04-23 08:07:12 -04:00
if ( ! ev . Handled )
2024-01-30 06:05:20 -05:00
{
2025-05-06 15:04:18 -04:00
ev . FulfillmentEntity = TryFulfillOrder ( ( station . Value , stationData ) , order . Account , order , orderDatabase ) ;
2024-04-23 08:07:12 -04:00
if ( ev . FulfillmentEntity = = null )
{
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-unfulfilled" ) ) ;
2024-04-23 08:07:12 -04:00
PlayDenySound ( uid , component ) ;
return ;
}
2024-01-30 06:05:20 -05:00
}
2024-07-29 08:19:43 +02:00
order . Approved = true ;
2025-04-13 09:22:36 -04:00
_audio . PlayPvs ( ApproveSound , uid ) ;
2024-01-30 06:05:20 -05:00
2025-01-30 05:05:47 +01:00
if ( ! _emag . CheckFlag ( uid , EmagType . Interaction ) )
2024-09-03 16:01:38 +03:00
{
var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent ( uid , player ) ;
RaiseLocalEvent ( tryGetIdentityShortInfoEvent ) ;
order . SetApproverData ( tryGetIdentityShortInfoEvent . Title ) ;
var message = Loc . GetString ( "cargo-console-unlock-approved-order-broadcast" ,
( "productName" , Loc . GetString ( order . ProductName ) ) ,
( "orderAmount" , order . OrderQuantity ) ,
( "approver" , order . Approver ? ? string . Empty ) ,
( "cost" , cost ) ) ;
2025-05-06 15:04:18 -04:00
_radio . SendRadioMessage ( uid , message , account . RadioChannel , uid , escapeMarkup : false ) ;
if ( CargoOrderConsoleComponent . BaseAnnouncementChannel ! = account . RadioChannel )
2025-04-13 09:22:36 -04:00
_radio . SendRadioMessage ( uid , message , CargoOrderConsoleComponent . BaseAnnouncementChannel , uid , escapeMarkup : false ) ;
2024-09-03 16:01:38 +03:00
}
2024-04-26 18:16:24 +10:00
ConsolePopup ( args . Actor , Loc . GetString ( "cargo-console-trade-station" , ( "destination" , MetaData ( ev . FulfillmentEntity . Value ) . EntityName ) ) ) ;
2024-01-30 06:05:20 -05:00
// Log order approval
2025-04-13 09:22:36 -04:00
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
2025-05-06 15:04:18 -04:00
$"{ToPrettyString(player):user} approved order [orderId:{order.OrderId}, quantity:{order.OrderQuantity}, product:{order.ProductId}, requester:{order.Requester}, reason:{order.Reason}] on account {order.Account} with balance at {accountBalance}" ) ;
2024-01-30 06:05:20 -05:00
2025-04-13 09:22:36 -04:00
orderDatabase . Orders [ component . Account ] . Remove ( order ) ;
2025-05-06 15:04:18 -04:00
UpdateBankAccount ( ( station . Value , bank ) , - cost , order . Account ) ;
2024-04-23 08:07:12 -04:00
UpdateOrders ( station . Value ) ;
2024-01-30 06:05:20 -05:00
}
2025-04-13 09:22:36 -04:00
private EntityUid ? TryFulfillOrder ( Entity < StationDataComponent > stationData , ProtoId < CargoAccountPrototype > account , CargoOrderData order , StationCargoOrderDatabaseComponent orderDatabase )
2024-01-30 06:05:20 -05:00
{
2024-01-19 13:02:28 +11:00
// No slots at the trade station
_listEnts . Clear ( ) ;
GetTradeStations ( stationData , ref _listEnts ) ;
EntityUid ? tradeDestination = null ;
// Try to fulfill from any station where possible, if the pad is not occupied.
foreach ( var trade in _listEnts )
{
2024-03-13 18:26:25 -05:00
var tradePads = GetCargoPallets ( trade , BuySellType . Buy ) ;
2024-01-19 13:02:28 +11:00
_random . Shuffle ( tradePads ) ;
var freePads = GetFreeCargoPallets ( trade , tradePads ) ;
2024-02-25 08:36:22 +01:00
if ( freePads . Count > = order . OrderQuantity ) //check if the station has enough free pallets
2024-01-19 13:02:28 +11:00
{
2024-02-25 08:36:22 +01:00
foreach ( var pad in freePads )
2024-01-19 13:02:28 +11:00
{
2024-02-25 08:36:22 +01:00
var coordinates = new EntityCoordinates ( trade , pad . Transform . LocalPosition ) ;
2025-04-13 09:22:36 -04:00
if ( FulfillOrder ( order , account , coordinates , orderDatabase . PrinterOutput ) )
2024-02-25 08:36:22 +01:00
{
tradeDestination = trade ;
order . NumDispatched + + ;
if ( order . OrderQuantity < = order . NumDispatched ) //Spawn a crate on free pellets until the order is fulfilled.
break ;
}
2024-01-19 13:02:28 +11:00
}
}
if ( tradeDestination ! = null )
break ;
}
2024-01-30 06:05:20 -05:00
return tradeDestination ;
2024-01-19 13:02:28 +11:00
}
private void GetTradeStations ( StationDataComponent data , ref List < EntityUid > ents )
{
foreach ( var gridUid in data . Grids )
{
if ( ! _tradeQuery . HasComponent ( gridUid ) )
continue ;
ents . Add ( gridUid ) ;
}
2022-06-23 14:36:47 +10:00
}
private void OnRemoveOrderMessage ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleRemoveOrderMessage args )
{
2024-01-19 13:02:28 +11:00
var station = _station . GetOwningStation ( uid ) ;
2025-05-06 15:04:18 -04:00
if ( component . SlipPrinter )
return ;
2024-01-19 13:02:28 +11:00
if ( ! TryGetOrderDatabase ( station , out var orderDatabase ) )
2023-07-08 09:02:17 -07:00
return ;
2025-04-13 09:22:36 -04:00
RemoveOrder ( station . Value , component . Account , args . OrderId , orderDatabase ) ;
2022-06-23 14:36:47 +10:00
}
2025-05-06 15:04:18 -04:00
private void OnAddOrderMessageSlipPrinter ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleAddOrderMessage args , CargoProductPrototype product )
{
if ( ! _protoMan . TryIndex ( component . Account , out var account ) )
return ;
if ( Timing . CurTime < component . NextPrintTime )
return ;
var label = Spawn ( account . AcquisitionSlip , Transform ( uid ) . Coordinates ) ;
component . NextPrintTime = Timing . CurTime + component . PrintDelay ;
_audio . PlayPvs ( component . PrintSound , uid ) ;
var paper = EnsureComp < PaperComponent > ( label ) ;
var msg = new FormattedMessage ( ) ;
msg . AddMarkupPermissive ( Loc . GetString ( "cargo-acquisition-slip-body" ,
( "product" , product . Name ) ,
( "description" , product . Description ) ,
( "unit" , product . Cost ) ,
( "amount" , args . Amount ) ,
( "cost" , product . Cost * args . Amount ) ,
( "orderer" , args . Requester ) ,
( "reason" , args . Reason ) ) ) ;
_paperSystem . SetContent ( ( label , paper ) , msg . ToMarkup ( ) ) ;
var slip = EnsureComp < CargoSlipComponent > ( label ) ;
slip . Product = product . ID ;
slip . Requester = args . Requester ;
slip . Reason = args . Reason ;
slip . OrderQuantity = args . Amount ;
slip . Account = component . Account ;
}
2022-06-23 14:36:47 +10:00
private void OnAddOrderMessage ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleAddOrderMessage args )
{
2024-04-26 18:16:24 +10:00
if ( args . Actor is not { Valid : true } player )
2022-12-15 12:33:27 -06:00
return ;
2022-06-23 14:36:47 +10:00
if ( args . Amount < = 0 )
return ;
2024-01-19 13:02:28 +11:00
var stationUid = _station . GetOwningStation ( uid ) ;
2023-06-16 05:19:02 +00:00
2024-01-19 13:02:28 +11:00
if ( ! TryGetOrderDatabase ( stationUid , out var orderDatabase ) )
2023-06-16 05:19:02 +00:00
return ;
if ( ! _protoMan . TryIndex < CargoProductPrototype > ( args . CargoProductId , out var product ) )
{
2023-07-10 05:24:48 +10:00
Log . Error ( $"Tried to add invalid cargo product {args.CargoProductId} as order!" ) ;
2023-06-16 05:19:02 +00:00
return ;
}
2022-06-23 14:36:47 +10:00
2024-01-19 22:47:08 -05:00
if ( ! component . AllowedGroups . Contains ( product . Group ) )
return ;
2025-05-06 15:04:18 -04:00
if ( component . SlipPrinter )
{
OnAddOrderMessageSlipPrinter ( uid , component , args , product ) ;
return ;
}
var data = GetOrderData ( args , product , GenerateOrderId ( orderDatabase ) , component . Account ) ;
2022-06-23 14:36:47 +10:00
2025-04-13 09:22:36 -04:00
if ( ! TryAddOrder ( stationUid . Value , component . Account , data , orderDatabase ) )
2022-06-23 14:36:47 +10:00
{
PlayDenySound ( uid , component ) ;
return ;
}
2022-12-15 12:33:27 -06:00
// Log order addition
2025-04-13 09:22:36 -04:00
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
2023-03-05 04:27:30 +00:00
$"{ToPrettyString(player):user} added order [orderId:{data.OrderId}, quantity:{data.OrderQuantity}, product:{data.ProductId}, requester:{data.Requester}, reason:{data.Reason}]" ) ;
2022-12-15 12:33:27 -06:00
2022-06-23 14:36:47 +10:00
}
private void OnOrderUIOpened ( EntityUid uid , CargoOrderConsoleComponent component , BoundUIOpenedEvent args )
{
var station = _station . GetOwningStation ( uid ) ;
2023-07-08 09:02:17 -07:00
UpdateOrderState ( uid , station ) ;
2022-06-23 14:36:47 +10:00
}
#endregion
2025-04-13 09:22:36 -04:00
private void UpdateOrderState ( EntityUid consoleUid , EntityUid ? station )
2024-07-13 06:11:14 +00:00
{
2025-04-13 09:22:36 -04:00
if ( ! TryComp < CargoOrderConsoleComponent > ( consoleUid , out var console ) )
2024-07-13 06:11:14 +00:00
return ;
2025-04-13 09:22:36 -04:00
if ( ! TryComp < StationCargoOrderDatabaseComponent > ( station , out var orderDatabase ) )
return ;
2022-06-23 14:36:47 +10:00
2024-04-26 18:16:24 +10:00
if ( _uiSystem . HasUi ( consoleUid , CargoConsoleUiKey . Orders ) )
2023-09-11 09:42:41 +10:00
{
2025-04-13 09:22:36 -04:00
_uiSystem . SetUiState ( consoleUid ,
CargoConsoleUiKey . Orders ,
new CargoConsoleInterfaceState (
2023-07-08 09:02:17 -07:00
MetaData ( station . Value ) . EntityName ,
2025-04-13 09:22:36 -04:00
GetOutstandingOrderCount ( orderDatabase , console . Account ) ,
2023-07-08 09:02:17 -07:00
orderDatabase . Capacity ,
2025-04-13 09:22:36 -04:00
GetNetEntity ( station . Value ) ,
orderDatabase . Orders [ console . Account ]
2023-07-08 09:02:17 -07:00
) ) ;
2023-09-11 09:42:41 +10:00
}
2022-06-23 14:36:47 +10:00
}
2024-04-26 18:16:24 +10:00
private void ConsolePopup ( EntityUid actor , string text )
2023-07-08 09:02:17 -07:00
{
2024-04-26 18:16:24 +10:00
_popup . PopupCursor ( text , actor ) ;
2023-07-08 09:02:17 -07:00
}
2022-06-23 14:36:47 +10:00
private void PlayDenySound ( EntityUid uid , CargoOrderConsoleComponent component )
{
2025-05-07 20:34:44 -04:00
if ( _timing . CurTime > = component . NextDenySoundTime )
{
component . NextDenySoundTime = _timing . CurTime + component . DenySoundDelay ;
_audio . PlayPvs ( _audio . ResolveSound ( component . ErrorSound ) , uid ) ;
}
2022-06-23 14:36:47 +10:00
}
2025-05-06 15:04:18 -04:00
private static CargoOrderData GetOrderData ( CargoConsoleAddOrderMessage args , CargoProductPrototype cargoProduct , int id , ProtoId < CargoAccountPrototype > account )
2022-06-23 14:36:47 +10:00
{
2025-05-06 15:04:18 -04:00
return new CargoOrderData ( id , cargoProduct . Product , cargoProduct . Name , cargoProduct . Cost , args . Amount , args . Requester , args . Reason , account ) ;
2022-06-23 14:36:47 +10:00
}
2025-04-13 09:22:36 -04:00
public static int GetOutstandingOrderCount ( StationCargoOrderDatabaseComponent component , ProtoId < CargoAccountPrototype > account )
2022-06-23 14:36:47 +10:00
{
var amount = 0 ;
2025-04-13 09:22:36 -04:00
foreach ( var order in component . Orders [ account ] )
2022-06-23 14:36:47 +10:00
{
2023-07-08 09:02:17 -07:00
if ( ! order . Approved )
continue ;
2023-03-05 04:27:30 +00:00
amount + = order . OrderQuantity - order . NumDispatched ;
2022-06-23 14:36:47 +10:00
}
return amount ;
}
/// <summary>
/// Updates all of the cargo-related consoles for a particular station.
/// This should be called whenever orders change.
/// </summary>
2024-04-23 08:07:12 -04:00
private void UpdateOrders ( EntityUid dbUid )
2022-06-23 14:36:47 +10:00
{
// Order added so all consoles need updating.
2023-03-23 16:10:49 +11:00
var orderQuery = AllEntityQuery < CargoOrderConsoleComponent > ( ) ;
2023-07-08 09:02:17 -07:00
while ( orderQuery . MoveNext ( out var uid , out var _ ) )
2022-06-23 14:36:47 +10:00
{
2023-03-23 16:10:49 +11:00
var station = _station . GetOwningStation ( uid ) ;
2023-07-08 09:02:17 -07:00
if ( station ! = dbUid )
2023-03-23 16:10:49 +11:00
continue ;
2022-06-23 14:36:47 +10:00
2023-07-08 09:02:17 -07:00
UpdateOrderState ( uid , station ) ;
2022-06-23 14:36:47 +10:00
}
}
2023-07-08 09:02:17 -07:00
public bool AddAndApproveOrder (
EntityUid dbUid ,
string spawnId ,
2024-04-18 03:32:21 +03:00
string name ,
2023-07-08 09:02:17 -07:00
int cost ,
int qty ,
string sender ,
string description ,
string dest ,
2024-01-30 06:05:20 -05:00
StationCargoOrderDatabaseComponent component ,
2025-04-13 09:22:36 -04:00
ProtoId < CargoAccountPrototype > account ,
2024-04-23 08:07:12 -04:00
Entity < StationDataComponent > stationData
2023-07-08 09:02:17 -07:00
)
2023-05-23 09:55:27 +12:00
{
2023-06-16 05:19:02 +00:00
DebugTools . Assert ( _protoMan . HasIndex < EntityPrototype > ( spawnId ) ) ;
2023-05-23 09:55:27 +12:00
// Make an order
var id = GenerateOrderId ( component ) ;
2025-05-06 15:04:18 -04:00
var order = new CargoOrderData ( id , spawnId , name , cost , qty , sender , description , account ) ;
2023-05-23 09:55:27 +12:00
// Approve it now
2023-06-16 05:19:02 +00:00
order . SetApproverData ( dest , sender ) ;
2024-07-29 08:19:43 +02:00
order . Approved = true ;
2023-05-23 09:55:27 +12:00
// Log order addition
2025-04-13 09:22:36 -04:00
_adminLogger . Add ( LogType . Action ,
LogImpact . Low ,
2023-05-23 09:55:27 +12:00
$"AddAndApproveOrder {description} added order [orderId:{order.OrderId}, quantity:{order.OrderQuantity}, product:{order.ProductId}, requester:{order.Requester}, reason:{order.Reason}]" ) ;
// Add it to the list
2025-04-13 09:22:36 -04:00
return TryAddOrder ( dbUid , account , order , component ) & & TryFulfillOrder ( stationData , account , order , component ) . HasValue ;
2023-05-23 09:55:27 +12:00
}
2025-04-13 09:22:36 -04:00
private bool TryAddOrder ( EntityUid dbUid , ProtoId < CargoAccountPrototype > account , CargoOrderData data , StationCargoOrderDatabaseComponent component )
2022-06-23 14:36:47 +10:00
{
2025-04-13 09:22:36 -04:00
component . Orders [ account ] . Add ( data ) ;
2024-04-23 08:07:12 -04:00
UpdateOrders ( dbUid ) ;
2022-06-23 14:36:47 +10:00
return true ;
}
2023-07-08 09:02:17 -07:00
private static int GenerateOrderId ( StationCargoOrderDatabaseComponent orderDB )
2022-06-23 14:36:47 +10:00
{
2023-05-23 09:55:27 +12:00
// We need an arbitrary unique ID to identify orders, since they may
2023-03-05 04:27:30 +00:00
// want to be cancelled later.
return + + orderDB . NumOrdersCreated ;
2022-06-23 14:36:47 +10:00
}
2025-04-13 09:22:36 -04:00
public void RemoveOrder ( EntityUid dbUid , ProtoId < CargoAccountPrototype > account , int index , StationCargoOrderDatabaseComponent orderDB )
2022-06-23 14:36:47 +10:00
{
2025-04-13 09:22:36 -04:00
var sequenceIdx = orderDB . Orders [ account ] . FindIndex ( order = > order . OrderId = = index ) ;
2023-03-05 04:27:30 +00:00
if ( sequenceIdx ! = - 1 )
{
2025-04-13 09:22:36 -04:00
orderDB . Orders [ account ] . RemoveAt ( sequenceIdx ) ;
2023-03-05 04:27:30 +00:00
}
2024-04-23 08:07:12 -04:00
UpdateOrders ( dbUid ) ;
2022-06-23 14:36:47 +10:00
}
public void ClearOrders ( StationCargoOrderDatabaseComponent component )
{
2024-01-19 13:02:28 +11:00
if ( component . Orders . Count = = 0 )
return ;
2022-06-23 14:36:47 +10:00
component . Orders . Clear ( ) ;
}
2025-04-13 09:22:36 -04:00
private static bool PopFrontOrder ( StationCargoOrderDatabaseComponent orderDB , ProtoId < CargoAccountPrototype > account , [ NotNullWhen ( true ) ] out CargoOrderData ? orderOut )
2023-03-05 04:27:30 +00:00
{
2025-04-13 09:22:36 -04:00
var orderIdx = orderDB . Orders [ account ] . FindIndex ( order = > order . Approved ) ;
2023-03-05 04:27:30 +00:00
if ( orderIdx = = - 1 )
{
orderOut = null ;
return false ;
}
2025-04-13 09:22:36 -04:00
orderOut = orderDB . Orders [ account ] [ orderIdx ] ;
2023-03-05 04:27:30 +00:00
orderOut . NumDispatched + + ;
2023-07-08 09:02:17 -07:00
if ( orderOut . NumDispatched > = orderOut . OrderQuantity )
2023-03-05 04:27:30 +00:00
{
// Order is complete. Remove from the queue.
2025-04-13 09:22:36 -04:00
orderDB . Orders [ account ] . RemoveAt ( orderIdx ) ;
2023-03-05 04:27:30 +00:00
}
return true ;
}
2024-01-19 13:02:28 +11:00
/// <summary>
/// Tries to fulfill the next outstanding order.
/// </summary>
2025-04-13 09:22:36 -04:00
[PublicAPI]
private bool FulfillNextOrder ( StationCargoOrderDatabaseComponent orderDB , ProtoId < CargoAccountPrototype > account , EntityCoordinates spawn , string? paperProto )
2023-03-05 04:27:30 +00:00
{
2025-04-13 09:22:36 -04:00
if ( ! PopFrontOrder ( orderDB , account , out var order ) )
2024-01-19 13:02:28 +11:00
return false ;
2025-04-13 09:22:36 -04:00
return FulfillOrder ( order , account , spawn , paperProto ) ;
2024-01-19 13:02:28 +11:00
}
2023-03-05 04:27:30 +00:00
2024-01-19 13:02:28 +11:00
/// <summary>
/// Fulfills the specified cargo order and spawns paper attached to it.
/// </summary>
2025-04-13 09:22:36 -04:00
private bool FulfillOrder ( CargoOrderData order , ProtoId < CargoAccountPrototype > account , EntityCoordinates spawn , string? paperProto )
2024-01-19 13:02:28 +11:00
{
// Create the item itself
var item = Spawn ( order . ProductId , spawn ) ;
2024-05-25 21:26:48 +01:00
// Ensure the item doesn't start anchored
2025-02-21 00:12:12 +11:00
_transformSystem . Unanchor ( item , Transform ( item ) ) ;
2024-05-25 21:26:48 +01:00
2024-01-19 13:02:28 +11:00
// Create a sheet of paper to write the order details on
var printed = EntityManager . SpawnEntity ( paperProto , spawn ) ;
if ( TryComp < PaperComponent > ( printed , out var paper ) )
{
// fill in the order data
var val = Loc . GetString ( "cargo-console-paper-print-name" , ( "orderNumber" , order . OrderId ) ) ;
_metaSystem . SetEntityName ( printed , val ) ;
2025-04-13 09:22:36 -04:00
var accountProto = _protoMan . Index ( account ) ;
_paperSystem . SetContent ( ( printed , paper ) ,
Loc . GetString (
2024-01-19 13:02:28 +11:00
"cargo-console-paper-print-text" ,
( "orderNumber" , order . OrderId ) ,
( "itemName" , MetaData ( item ) . EntityName ) ,
2024-06-13 20:36:38 +02:00
( "orderQuantity" , order . OrderQuantity ) ,
2024-01-19 13:02:28 +11:00
( "requester" , order . Requester ) ,
2025-04-13 09:22:36 -04:00
( "reason" , string . IsNullOrWhiteSpace ( order . Reason ) ? Loc . GetString ( "cargo-console-paper-reason-default" ) : order . Reason ) ,
( "account" , Loc . GetString ( accountProto . Name ) ) ,
( "accountcode" , Loc . GetString ( accountProto . Code ) ) ,
( "approver" , string . IsNullOrWhiteSpace ( order . Approver ) ? Loc . GetString ( "cargo-console-paper-approver-default" ) : order . Approver ) ) ) ;
2024-01-19 13:02:28 +11:00
// attempt to attach the label to the item
if ( TryComp < PaperLabelComponent > ( item , out var label ) )
2023-03-05 04:27:30 +00:00
{
2024-01-19 13:02:28 +11:00
_slots . TryInsert ( item , label . LabelSlot , printed , null ) ;
2023-03-05 04:27:30 +00:00
}
}
2024-01-19 13:02:28 +11:00
return true ;
2023-03-05 04:27:30 +00:00
}
2022-06-23 14:36:47 +10:00
#region Station
2024-01-19 13:02:28 +11:00
private bool TryGetOrderDatabase ( [ NotNullWhen ( true ) ] EntityUid ? stationUid , [ MaybeNullWhen ( false ) ] out StationCargoOrderDatabaseComponent dbComp )
2022-06-23 14:36:47 +10:00
{
2024-01-19 13:02:28 +11:00
return TryComp ( stationUid , out dbComp ) ;
2022-06-23 14:36:47 +10:00
}
#endregion
}
}