2022-08-07 20:21:56 -03:00
using Content.Server.Administration.Logs ;
2023-12-29 04:47:43 -08:00
using Content.Server.Chemistry.Containers.EntitySystems ;
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 ;
2023-12-29 04:47:43 -08:00
using Content.Shared.Verbs ;
using JetBrains.Annotations ;
using Robust.Server.GameObjects ;
2023-10-29 04:21:02 +11:00
using Robust.Shared.Player ;
2021-10-05 14:29:03 +11:00
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>
2023-12-29 04:47:43 -08:00
public static readonly List < int > DefaultTransferAmounts = new ( ) { 1 , 5 , 10 , 25 , 50 , 100 , 250 , 500 , 1000 } ;
2021-10-05 14:29:03 +11:00
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 ) ;
}
2023-12-29 04:47:43 -08:00
private void OnTransferAmountSetValueMessage ( Entity < SolutionTransferComponent > entity , ref TransferAmountSetValueMessage message )
2023-02-19 02:01:05 +01:00
{
2023-12-29 04:47:43 -08:00
var newTransferAmount = FixedPoint2 . Clamp ( message . Value , entity . Comp . MinimumTransferAmount , entity . Comp . MaximumTransferAmount ) ;
entity . Comp . TransferAmount = newTransferAmount ;
2023-02-19 02:01:05 +01:00
2023-12-29 04:47:43 -08:00
if ( message . Session . AttachedEntity is { Valid : true } user )
_popupSystem . PopupEntity ( Loc . GetString ( "comp-solution-transfer-set-amount" , ( "amount" , newTransferAmount ) ) , entity . Owner , user ) ;
2021-10-05 14:29:03 +11:00
}
2023-12-29 04:47:43 -08:00
private void AddSetTransferVerbs ( Entity < SolutionTransferComponent > entity , ref GetVerbsEvent < AlternativeVerb > args )
2021-10-05 14:29:03 +11:00
{
2023-12-29 04:47:43 -08:00
var ( uid , component ) = entity ;
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 ;
2023-08-24 03:10:55 -07:00
if ( ! EntityManager . TryGetComponent ( args . User , out ActorComponent ? 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-12-29 04:47:43 -08:00
custom . Act = ( ) = > _userInterfaceSystem . TryOpen ( uid , 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 ;
2023-12-29 04:47:43 -08:00
var user = args . User ;
2021-10-05 14:29:03 +11:00
foreach ( var amount in DefaultTransferAmounts )
{
2023-12-29 04:47:43 -08:00
if ( amount < component . MinimumTransferAmount . Int ( ) | | amount > component . MaximumTransferAmount . Int ( ) )
2021-10-05 14:29:03 +11:00
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-12-29 04:47:43 -08:00
_popupSystem . PopupEntity ( Loc . GetString ( "comp-solution-transfer-set-amount" , ( "amount" , amount ) ) , uid , 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
2023-12-29 04:47:43 -08:00
private void OnAfterInteract ( Entity < SolutionTransferComponent > entity , ref AfterInteractEvent args )
2022-07-14 04:45:31 -07:00
{
if ( ! args . CanReach | | args . Target = = null )
return ;
var target = args . Target ! . Value ;
2023-12-29 04:47:43 -08:00
var ( uid , component ) = entity ;
2022-07-14 04:45:31 -07:00
//Special case for reagent tanks, because normally clicking another container will give solution, not take it.
2023-12-29 04:47:43 -08:00
if ( component . CanReceive & & ! EntityManager . HasComponent < RefillableSolutionComponent > ( target ) // target must not be refillable (e.g. Reagent Tanks)
& & _solutionContainerSystem . TryGetDrainableSolution ( target , out var targetSoln , out _ ) // target must be drainable
& & EntityManager . TryGetComponent ( uid , out RefillableSolutionComponent ? refillComp )
& & _solutionContainerSystem . TryGetRefillableSolution ( ( uid , refillComp , null ) , out var ownerSoln , out var ownerRefill ) )
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
}
2023-12-29 04:47:43 -08:00
var transferred = Transfer ( args . User , target , targetSoln . Value , uid , ownerSoln . Value , transferAmount ) ;
2022-07-14 04:45:31 -07:00
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-12-29 04:47:43 -08:00
if ( component . CanSend & & _solutionContainerSystem . TryGetRefillableSolution ( target , out targetSoln , out var targetRefill )
& & _solutionContainerSystem . TryGetDrainableSolution ( uid , out ownerSoln , 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 ) ;
}
2023-12-29 04:47:43 -08:00
var transferred = Transfer ( args . User , uid , ownerSoln . Value , target , targetSoln . Value , transferAmount ) ;
2022-07-14 04:45:31 -07:00
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 ,
2023-12-29 04:47:43 -08:00
Entity < SolutionComponent > source ,
2022-04-15 23:17:48 +02:00
EntityUid targetEntity ,
2023-12-29 04:47:43 -08:00
Entity < SolutionComponent > target ,
2022-04-15 23:17:48 +02:00
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-12-29 04:47:43 -08:00
var sourceSolution = source . Comp . Solution ;
if ( sourceSolution . 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 ;
}
2023-12-29 04:47:43 -08:00
var targetSolution = target . Comp . Solution ;
if ( targetSolution . AvailableVolume = = 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-full" , ( "target" , targetEntity ) ) , targetEntity , user ) ;
2022-04-15 23:17:48 +02:00
return FixedPoint2 . Zero ;
}
2023-12-29 04:47:43 -08:00
var actualAmount = FixedPoint2 . Min ( amount , FixedPoint2 . Min ( sourceSolution . Volume , targetSolution . 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 ,
2023-12-29 04:47:43 -08:00
$"{EntityManager.ToPrettyString(user):player} transferred {string.Join(" , ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {SolutionContainerSystem.ToPrettyString(targetSolution)}" ) ;
2022-08-07 20:21:56 -03:00
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
}
}