2022-08-07 20:21:56 -03:00
using Content.Server.Administration.Logs ;
2021-10-05 14:29:03 +11:00
using Content.Shared.Verbs ;
2022-07-14 04:45:31 -07:00
using Content.Server.Chemistry.Components.SolutionManager ;
2021-10-05 14:29:03 +11:00
using JetBrains.Annotations ;
using Robust.Server.GameObjects ;
2023-02-19 02:01:05 +01:00
using Content.Shared.Chemistry ;
2022-04-15 23:17:48 +02:00
using Content.Shared.Chemistry.Components ;
2022-08-07 20:21:56 -03:00
using Content.Shared.Database ;
2021-11-03 16:48:03 -07:00
using Content.Shared.FixedPoint ;
2022-07-14 04:45:31 -07:00
using Content.Shared.Interaction ;
2021-10-05 14:29:03 +11:00
using Content.Shared.Popups ;
namespace Content.Server.Chemistry.EntitySystems
{
2022-05-13 00:59:03 -07:00
[UsedImplicitly]
2022-02-16 00:23:23 -07:00
public sealed class SolutionTransferSystem : EntitySystem
2021-10-05 14:29:03 +11:00
{
2023-02-19 02:01:05 +01:00
[Dependency] private readonly SharedPopupSystem _popupSystem = default ! ;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default ! ;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default ! ;
2022-08-07 20:21:56 -03:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2022-07-14 04:45:31 -07:00
2021-10-05 14:29:03 +11:00
/// <summary>
/// Default transfer amounts for the set-transfer verb.
/// </summary>
public static readonly List < int > DefaultTransferAmounts = new ( ) { 1 , 5 , 10 , 25 , 50 , 100 , 250 , 500 , 1000 } ;
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-02-10 15:30:59 +13:00
SubscribeLocalEvent < SolutionTransferComponent , GetVerbsEvent < AlternativeVerb > > ( AddSetTransferVerbs ) ;
2022-07-14 04:45:31 -07:00
SubscribeLocalEvent < SolutionTransferComponent , AfterInteractEvent > ( OnAfterInteract ) ;
2023-02-19 02:01:05 +01:00
SubscribeLocalEvent < SolutionTransferComponent , TransferAmountSetValueMessage > ( OnTransferAmountSetValueMessage ) ;
}
private void OnTransferAmountSetValueMessage ( EntityUid uid , SolutionTransferComponent solutionTransfer , TransferAmountSetValueMessage message )
{
var newTransferAmount = FixedPoint2 . Clamp ( message . Value , solutionTransfer . MinimumTransferAmount , solutionTransfer . MaximumTransferAmount ) ;
solutionTransfer . TransferAmount = newTransferAmount ;
if ( message . Session . AttachedEntity is { Valid : true } user )
_popupSystem . PopupEntity ( Loc . GetString ( "comp-solution-transfer-set-amount" , ( "amount" , newTransferAmount ) ) , uid , user ) ;
2021-10-05 14:29:03 +11:00
}
2022-02-10 15:30:59 +13:00
private void AddSetTransferVerbs ( EntityUid uid , SolutionTransferComponent component , GetVerbsEvent < AlternativeVerb > args )
2021-10-05 14:29:03 +11:00
{
2022-06-26 21:07:07 -07:00
if ( ! args . CanAccess | | ! args . CanInteract | | ! component . CanChangeTransferAmount | | args . Hands = = null )
2021-10-05 14:29:03 +11:00
return ;
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . TryGetComponent < ActorComponent ? > ( args . User , out var actor ) )
2021-10-05 14:29:03 +11:00
return ;
2021-12-03 14:05:23 +01:00
2021-10-05 14:29:03 +11:00
// Custom transfer verb
2022-02-10 15:30:59 +13:00
AlternativeVerb custom = new ( ) ;
2021-10-05 14:29:03 +11:00
custom . Text = Loc . GetString ( "comp-solution-transfer-verb-custom-amount" ) ;
custom . Category = VerbCategory . SetTransferAmount ;
2023-02-19 02:01:05 +01:00
custom . Act = ( ) = > _userInterfaceSystem . TryOpen ( args . Target , TransferAmountUiKey . Key , actor . PlayerSession ) ;
2021-10-05 14:29:03 +11:00
custom . Priority = 1 ;
args . Verbs . Add ( custom ) ;
// Add specific transfer verbs according to the container's size
var priority = 0 ;
foreach ( var amount in DefaultTransferAmounts )
{
if ( amount < component . MinimumTransferAmount . Int ( ) | | amount > component . MaximumTransferAmount . Int ( ) )
continue ;
2022-02-10 15:30:59 +13:00
AlternativeVerb verb = new ( ) ;
2021-10-05 14:29:03 +11:00
verb . Text = Loc . GetString ( "comp-solution-transfer-verb-amount" , ( "amount" , amount ) ) ;
verb . Category = VerbCategory . SetTransferAmount ;
verb . Act = ( ) = >
{
2021-11-03 16:48:03 -07:00
component . TransferAmount = FixedPoint2 . New ( amount ) ;
2023-02-19 02:01:05 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( "comp-solution-transfer-set-amount" , ( "amount" , amount ) ) , uid , args . User ) ;
2021-10-05 14:29:03 +11:00
} ;
// we want to sort by size, not alphabetically by the verb text.
verb . Priority = priority ;
priority - - ;
2021-12-03 14:05:23 +01:00
2021-10-05 14:29:03 +11:00
args . Verbs . Add ( verb ) ;
}
}
2022-04-15 23:17:48 +02:00
2022-07-14 04:45:31 -07:00
private void OnAfterInteract ( EntityUid uid , SolutionTransferComponent component , AfterInteractEvent args )
{
if ( ! args . CanReach | | args . Target = = null )
return ;
var target = args . Target ! . Value ;
//Special case for reagent tanks, because normally clicking another container will give solution, not take it.
if ( component . CanReceive & & ! EntityManager . HasComponent < RefillableSolutionComponent > ( target ) // target must not be refillable (e.g. Reagent Tanks)
2023-02-19 02:01:05 +01:00
& & _solutionContainerSystem . TryGetDrainableSolution ( target , out var targetDrain ) // target must be drainable
2022-07-14 04:45:31 -07:00
& & EntityManager . TryGetComponent ( uid , out RefillableSolutionComponent ? refillComp )
2023-02-19 02:01:05 +01:00
& & _solutionContainerSystem . TryGetRefillableSolution ( uid , out var ownerRefill , refillable : refillComp ) )
2022-07-14 04:45:31 -07:00
{
var transferAmount = component . TransferAmount ; // This is the player-configurable transfer amount of "uid," not the target reagent tank.
if ( EntityManager . TryGetComponent ( uid , out RefillableSolutionComponent ? refill ) & & refill . MaxRefill ! = null ) // uid is the entity receiving solution from target.
{
transferAmount = FixedPoint2 . Min ( transferAmount , ( FixedPoint2 ) refill . MaxRefill ) ; // if the receiver has a smaller transfer limit, use that instead
}
var transferred = Transfer ( args . User , target , targetDrain , uid , ownerRefill , transferAmount ) ;
if ( transferred > 0 )
{
var toTheBrim = ownerRefill . AvailableVolume = = 0 ;
var msg = toTheBrim
? "comp-solution-transfer-fill-fully"
: "comp-solution-transfer-fill-normal" ;
2023-02-19 02:01:05 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( msg , ( "owner" , args . Target ) , ( "amount" , transferred ) , ( "target" , uid ) ) , uid , args . User ) ;
2022-07-14 04:45:31 -07:00
args . Handled = true ;
return ;
}
}
// if target is refillable, and owner is drainable
2023-02-19 02:01:05 +01:00
if ( component . CanSend & & _solutionContainerSystem . TryGetRefillableSolution ( target , out var targetRefill )
& & _solutionContainerSystem . TryGetDrainableSolution ( uid , out var ownerDrain ) )
2022-07-14 04:45:31 -07:00
{
var transferAmount = component . TransferAmount ;
if ( EntityManager . TryGetComponent ( target , out RefillableSolutionComponent ? refill ) & & refill . MaxRefill ! = null )
{
transferAmount = FixedPoint2 . Min ( transferAmount , ( FixedPoint2 ) refill . MaxRefill ) ;
}
var transferred = Transfer ( args . User , uid , ownerDrain , target , targetRefill , transferAmount ) ;
if ( transferred > 0 )
{
2023-02-19 02:01:05 +01:00
var message = Loc . GetString ( "comp-solution-transfer-transfer-solution" , ( "amount" , transferred ) , ( "target" , target ) ) ;
_popupSystem . PopupEntity ( message , uid , args . User ) ;
2022-07-14 04:45:31 -07:00
args . Handled = true ;
}
}
}
2022-04-15 23:17:48 +02:00
/// <summary>
/// Transfer from a solution to another.
/// </summary>
/// <returns>The actual amount transferred.</returns>
public FixedPoint2 Transfer ( EntityUid user ,
EntityUid sourceEntity ,
Solution source ,
EntityUid targetEntity ,
Solution target ,
FixedPoint2 amount )
{
var transferAttempt = new SolutionTransferAttemptEvent ( sourceEntity , targetEntity ) ;
// Check if the source is cancelling the transfer
2023-02-19 02:01:05 +01:00
RaiseLocalEvent ( sourceEntity , transferAttempt , broadcast : true ) ;
2022-04-15 23:17:48 +02:00
if ( transferAttempt . Cancelled )
{
2023-02-19 02:01:05 +01:00
_popupSystem . PopupEntity ( transferAttempt . CancelReason ! , sourceEntity , user ) ;
2022-04-15 23:17:48 +02:00
return FixedPoint2 . Zero ;
}
2023-01-12 16:41:40 +13:00
if ( source . Volume = = 0 )
2022-04-15 23:17:48 +02:00
{
2023-02-19 02:01:05 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( "comp-solution-transfer-is-empty" , ( "target" , sourceEntity ) ) , sourceEntity , user ) ;
2022-04-15 23:17:48 +02:00
return FixedPoint2 . Zero ;
}
// Check if the target is cancelling the transfer
2023-02-19 02:01:05 +01:00
RaiseLocalEvent ( targetEntity , transferAttempt , broadcast : true ) ;
2022-04-15 23:17:48 +02:00
if ( transferAttempt . Cancelled )
{
2023-02-19 02:01:05 +01:00
_popupSystem . PopupEntity ( transferAttempt . CancelReason ! , sourceEntity , user ) ;
2022-04-15 23:17:48 +02:00
return FixedPoint2 . Zero ;
}
if ( target . AvailableVolume = = 0 )
{
2023-02-19 02:01:05 +01:00
_popupSystem . PopupEntity ( Loc . GetString ( "comp-solution-transfer-is-full" , ( "target" , targetEntity ) ) , targetEntity , user ) ;
2022-04-15 23:17:48 +02:00
return FixedPoint2 . Zero ;
}
2023-01-12 16:41:40 +13:00
var actualAmount = FixedPoint2 . Min ( amount , FixedPoint2 . Min ( source . Volume , target . AvailableVolume ) ) ;
2022-04-15 23:17:48 +02:00
2023-02-19 02:01:05 +01:00
var solution = _solutionContainerSystem . Drain ( sourceEntity , source , actualAmount ) ;
_solutionContainerSystem . Refill ( targetEntity , target , solution ) ;
2022-04-15 23:17:48 +02:00
2022-08-07 20:21:56 -03:00
_adminLogger . Add ( LogType . Action , LogImpact . Medium ,
$"{EntityManager.ToPrettyString(user):player} transferred {string.Join(" , ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {string.Join(" , ", target.Contents)}" ) ;
2022-04-15 23:17:48 +02:00
return actualAmount ;
}
}
/// <summary>
/// Raised when attempting to transfer from one solution to another.
/// </summary>
public sealed class SolutionTransferAttemptEvent : CancellableEntityEventArgs
{
public SolutionTransferAttemptEvent ( EntityUid from , EntityUid to )
{
From = from ;
To = to ;
}
public EntityUid From { get ; }
public EntityUid To { get ; }
/// <summary>
/// Why the transfer has been cancelled.
/// </summary>
public string? CancelReason { get ; private set ; }
/// <summary>
/// Cancels the transfer.
/// </summary>
public void Cancel ( string reason )
{
base . Cancel ( ) ;
CancelReason = reason ;
}
2021-10-05 14:29:03 +11:00
}
}