2022-06-23 14:36:47 +10:00
using System.Linq ;
using Content.Server.Access.Systems ;
using Content.Server.Cargo.Components ;
using Content.Server.MachineLinking.System ;
using Content.Server.Popups ;
using Content.Server.Station.Systems ;
using Content.Shared.Access.Systems ;
2022-12-15 12:33:27 -06:00
using Content.Shared.Administration.Logs ;
2022-06-23 14:36:47 +10:00
using Content.Shared.Cargo ;
using Content.Shared.Cargo.BUI ;
using Content.Shared.Cargo.Events ;
using Content.Shared.Cargo.Prototypes ;
2022-12-15 12:33:27 -06:00
using Content.Shared.Database ;
2022-06-23 14:36:47 +10:00
using Content.Shared.GameTicking ;
using Robust.Server.GameObjects ;
using Robust.Shared.Audio ;
using Robust.Shared.Player ;
using Robust.Shared.Players ;
namespace Content.Server.Cargo.Systems
{
public sealed partial class CargoSystem
{
/// <summary>
/// How much time to wait (in seconds) before increasing bank accounts balance.
/// </summary>
private const int Delay = 10 ;
/// <summary>
/// Keeps track of how much time has elapsed since last balance increase.
/// </summary>
private float _timer ;
[Dependency] private readonly IdCardSystem _idCardSystem = default ! ;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default ! ;
[Dependency] private readonly SignalLinkerSystem _linker = default ! ;
[Dependency] private readonly PopupSystem _popup = default ! ;
[Dependency] private readonly StationSystem _station = default ! ;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default ! ;
2022-12-15 12:33:27 -06:00
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default ! ;
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 ) ;
SubscribeLocalEvent < RoundRestartCleanupEvent > ( Reset ) ;
Reset ( ) ;
}
private void OnInit ( EntityUid uid , CargoOrderConsoleComponent orderConsole , ComponentInit args )
{
var station = _station . GetOwningStation ( uid ) ;
UpdateOrderState ( orderConsole , station ) ;
}
private void Reset ( RoundRestartCleanupEvent ev )
{
Reset ( ) ;
}
private void Reset ( )
{
_timer = 0 ;
}
private void UpdateConsole ( float frameTime )
{
_timer + = frameTime ;
while ( _timer > Delay )
{
_timer - = Delay ;
foreach ( var account in EntityQuery < StationBankAccountComponent > ( ) )
{
account . Balance + = account . IncreasePerSecond * Delay ;
}
foreach ( var comp in EntityQuery < CargoOrderConsoleComponent > ( ) )
{
if ( ! _uiSystem . IsUiOpen ( comp . Owner , CargoConsoleUiKey . Orders ) ) continue ;
var station = _station . GetOwningStation ( comp . Owner ) ;
UpdateOrderState ( comp , station ) ;
}
}
}
#region Interface
private void OnApproveOrderMessage ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleApproveOrderMessage args )
{
if ( args . Session . AttachedEntity is not { Valid : true } player )
return ;
if ( ! _accessReaderSystem . IsAllowed ( player , uid ) )
{
ConsolePopup ( args . Session , Loc . GetString ( "cargo-console-order-not-allowed" ) ) ;
PlayDenySound ( uid , component ) ;
return ;
}
var orderDatabase = GetOrderDatabase ( component ) ;
var bankAccount = GetBankAccount ( component ) ;
// No station to deduct from.
if ( orderDatabase = = null | | bankAccount = = null )
{
ConsolePopup ( args . Session , Loc . GetString ( "cargo-console-station-not-found" ) ) ;
PlayDenySound ( uid , component ) ;
return ;
}
// No order to approve?
if ( ! orderDatabase . Orders . TryGetValue ( args . OrderNumber , out var order ) | |
order . Approved ) return ;
// Invalid order
if ( ! _protoMan . TryIndex < CargoProductPrototype > ( order . ProductId , out var product ) )
{
ConsolePopup ( args . Session , Loc . GetString ( "cargo-console-invalid-product" ) ) ;
PlayDenySound ( uid , component ) ;
return ;
}
var amount = GetOrderCount ( orderDatabase ) ;
var capacity = orderDatabase . Capacity ;
// Too many orders, avoid them getting spammed in the UI.
if ( amount > = capacity )
{
ConsolePopup ( args . Session , Loc . GetString ( "cargo-console-too-many" ) ) ;
PlayDenySound ( uid , component ) ;
return ;
}
// Cap orders so someone can't spam thousands.
var orderAmount = Math . Min ( capacity - amount , order . Amount ) ;
if ( orderAmount ! = order . Amount )
{
order . Amount = orderAmount ;
ConsolePopup ( args . Session , Loc . GetString ( "cargo-console-snip-snip" ) ) ;
PlayDenySound ( uid , component ) ;
}
var cost = product . PointCost * order . Amount ;
// Not enough balance
if ( cost > bankAccount . Balance )
{
ConsolePopup ( args . Session , Loc . GetString ( "cargo-console-insufficient-funds" , ( "cost" , cost ) ) ) ;
PlayDenySound ( uid , component ) ;
return ;
}
_idCardSystem . TryFindIdCard ( player , out var idCard ) ;
2022-07-09 16:48:57 +03:00
order . SetApproverData ( idCard ) ;
2023-01-10 04:55:59 -05:00
_audio . PlayPvs ( _audio . GetSound ( component . ConfirmSound ) , uid ) ;
2022-06-23 14:36:47 +10:00
2022-12-15 12:33:27 -06:00
// Log order approval
_adminLogger . Add ( LogType . Action , LogImpact . Low ,
$"{ToPrettyString(player):user} approved order [orderNum:{order.OrderNumber}, amount:{order.Amount}, product:{order.ProductId}, requester:{order.Requester}, reason:{order.Reason}] with balance at {bankAccount.Balance}" ) ;
2022-06-23 14:36:47 +10:00
DeductFunds ( bankAccount , cost ) ;
UpdateOrders ( orderDatabase ) ;
}
private void OnRemoveOrderMessage ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleRemoveOrderMessage args )
{
var orderDatabase = GetOrderDatabase ( component ) ;
if ( orderDatabase = = null ) return ;
RemoveOrder ( orderDatabase , args . OrderNumber ) ;
}
private void OnAddOrderMessage ( EntityUid uid , CargoOrderConsoleComponent component , CargoConsoleAddOrderMessage args )
{
2022-12-15 12:33:27 -06:00
if ( args . Session . AttachedEntity is not { Valid : true } player )
return ;
2022-06-23 14:36:47 +10:00
if ( args . Amount < = 0 )
return ;
var bank = GetBankAccount ( component ) ;
if ( bank = = null ) return ;
var orderDatabase = GetOrderDatabase ( component ) ;
if ( orderDatabase = = null ) return ;
var data = GetOrderData ( args , GetNextIndex ( orderDatabase ) ) ;
if ( ! TryAddOrder ( orderDatabase , data ) )
{
PlayDenySound ( uid , component ) ;
return ;
}
2022-12-15 12:33:27 -06:00
// Log order addition
_adminLogger . Add ( LogType . Action , LogImpact . Low ,
$"{ToPrettyString(player):user} added order [orderNum:{data.OrderNumber}, amount:{data.Amount}, product:{data.ProductId}, requester:{data.Requester}, reason:{data.Reason}]" ) ;
2022-06-23 14:36:47 +10:00
}
private void OnOrderUIOpened ( EntityUid uid , CargoOrderConsoleComponent component , BoundUIOpenedEvent args )
{
var station = _station . GetOwningStation ( uid ) ;
UpdateOrderState ( component , station ) ;
}
#endregion
private void UpdateOrderState ( CargoOrderConsoleComponent component , EntityUid ? station )
{
if ( station = = null | |
! TryComp < StationCargoOrderDatabaseComponent > ( station , out var orderDatabase ) | |
! TryComp < StationBankAccountComponent > ( station , out var bankAccount ) ) return ;
var state = new CargoConsoleInterfaceState (
MetaData ( station . Value ) . EntityName ,
GetOrderCount ( orderDatabase ) ,
orderDatabase . Capacity ,
bankAccount . Balance ,
orderDatabase . Orders . Values . ToList ( ) ) ;
_uiSystem . GetUiOrNull ( component . Owner , CargoConsoleUiKey . Orders ) ? . SetState ( state ) ;
}
2022-12-19 10:41:47 +13:00
private void ConsolePopup ( ICommonSession session , string text ) = > _popup . PopupCursor ( text , session ) ;
2022-06-23 14:36:47 +10:00
private void PlayDenySound ( EntityUid uid , CargoOrderConsoleComponent component )
{
2023-01-10 04:55:59 -05:00
_audio . PlayPvs ( _audio . GetSound ( component . ErrorSound ) , uid ) ;
2022-06-23 14:36:47 +10:00
}
private CargoOrderData GetOrderData ( CargoConsoleAddOrderMessage args , int index )
{
2022-07-09 16:48:57 +03:00
return new CargoOrderData ( index , args . ProductId , args . Amount , args . Requester , args . Reason ) ;
2022-06-23 14:36:47 +10:00
}
private int GetOrderCount ( StationCargoOrderDatabaseComponent component )
{
var amount = 0 ;
foreach ( var ( _ , order ) in component . Orders )
{
if ( ! order . Approved ) continue ;
amount + = order . Amount ;
}
return amount ;
}
/// <summary>
/// Updates all of the cargo-related consoles for a particular station.
/// This should be called whenever orders change.
/// </summary>
private void UpdateOrders ( StationCargoOrderDatabaseComponent component )
{
// Order added so all consoles need updating.
foreach ( var comp in EntityQuery < CargoOrderConsoleComponent > ( true ) )
{
var station = _station . GetOwningStation ( component . Owner ) ;
if ( station ! = component . Owner ) continue ;
UpdateOrderState ( comp , station ) ;
}
foreach ( var comp in EntityQuery < CargoShuttleConsoleComponent > ( true ) )
{
var station = _station . GetOwningStation ( component . Owner ) ;
if ( station ! = component . Owner ) continue ;
UpdateShuttleState ( comp , station ) ;
}
}
public bool TryAddOrder ( StationCargoOrderDatabaseComponent component , CargoOrderData data )
{
component . Orders . Add ( data . OrderNumber , data ) ;
UpdateOrders ( component ) ;
return true ;
}
private int GetNextIndex ( StationCargoOrderDatabaseComponent component )
{
var index = component . Index ;
component . Index + + ;
return index ;
}
public void RemoveOrder ( StationCargoOrderDatabaseComponent component , int index )
{
if ( ! component . Orders . Remove ( index ) ) return ;
UpdateOrders ( component ) ;
}
public void ClearOrders ( StationCargoOrderDatabaseComponent component )
{
if ( component . Orders . Count = = 0 ) return ;
component . Orders . Clear ( ) ;
Dirty ( component ) ;
}
private void DeductFunds ( StationBankAccountComponent component , int amount )
{
component . Balance = Math . Max ( 0 , component . Balance - amount ) ;
Dirty ( component ) ;
}
#region Station
private StationBankAccountComponent ? GetBankAccount ( CargoOrderConsoleComponent component )
{
var station = _station . GetOwningStation ( component . Owner ) ;
TryComp < StationBankAccountComponent > ( station , out var bankComponent ) ;
return bankComponent ;
}
private StationCargoOrderDatabaseComponent ? GetOrderDatabase ( CargoOrderConsoleComponent component )
{
var station = _station . GetOwningStation ( component . Owner ) ;
TryComp < StationCargoOrderDatabaseComponent > ( station , out var orderComponent ) ;
return orderComponent ;
}
#endregion
}
}