2021-05-04 15:37:16 +02:00
using System.Threading.Tasks ;
2021-10-29 13:40:15 +01:00
using Content.Server.Chemistry.EntitySystems ;
2021-06-09 22:19:39 +02:00
using Content.Server.DoAfter ;
2021-10-27 09:24:18 +01:00
using Content.Server.Fluids.EntitySystems ;
2021-09-06 15:49:44 +02:00
using Content.Shared.Chemistry.Components ;
2021-11-03 16:48:03 -07:00
using Content.Shared.FixedPoint ;
2021-06-09 22:19:39 +02:00
using Content.Shared.Interaction ;
using Content.Shared.Interaction.Helpers ;
2021-09-26 15:18:45 +02:00
using Content.Shared.Popups ;
2021-07-10 17:35:33 +02:00
using Content.Shared.Sound ;
2021-05-04 15:37:16 +02:00
using Robust.Shared.Audio ;
2020-04-22 04:23:12 +10:00
using Robust.Shared.GameObjects ;
2021-12-03 11:30:03 +01:00
using Robust.Shared.IoC ;
2020-04-22 04:23:12 +10:00
using Robust.Shared.Localization ;
2021-03-21 09:12:03 -07:00
using Robust.Shared.Player ;
2021-03-05 01:08:38 +01:00
using Robust.Shared.Serialization.Manager.Attributes ;
2021-05-04 15:37:16 +02:00
using Robust.Shared.ViewVariables ;
2020-04-22 04:23:12 +10:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Fluids.Components
2020-04-22 04:23:12 +10:00
{
/// <summary>
/// For cleaning up puddles
/// </summary>
[RegisterComponent]
2022-02-16 00:23:23 -07:00
public sealed class MopComponent : Component , IAfterInteract
2020-04-22 04:23:12 +10:00
{
2021-12-05 21:02:04 +01:00
[Dependency] private readonly IEntityManager _entities = default ! ;
2021-09-06 15:49:44 +02:00
public const string SolutionName = "mop" ;
2022-02-16 00:23:23 -07:00
2021-02-25 11:32:08 +11:00
/// <summary>
/// Used to prevent do_after spam if we're currently mopping.
/// </summary>
2021-03-05 01:08:38 +01:00
public bool Mopping { get ; private set ; }
2021-02-25 11:32:08 +11:00
2022-01-22 23:27:22 -06:00
// MopSolution Object stores whatever solution the mop has absorbed.
2021-09-06 15:49:44 +02:00
public Solution ? MopSolution
{
get
{
2021-12-03 15:53:09 +01:00
EntitySystem . Get < SolutionContainerSystem > ( ) . TryGetSolution ( Owner , SolutionName , out var solution ) ;
2021-09-06 15:49:44 +02:00
return solution ;
}
}
2020-04-22 04:23:12 +10:00
2022-01-22 23:27:22 -06:00
// MaxVolume is the Maximum volume the mop can absorb (however, this is defined in janitor.yml)
2021-11-03 16:48:03 -07:00
public FixedPoint2 MaxVolume
2020-04-22 04:23:12 +10:00
{
2021-11-03 16:48:03 -07:00
get = > MopSolution ? . MaxVolume ? ? FixedPoint2 . Zero ;
2020-08-24 13:39:00 +02:00
set
{
2021-09-06 15:49:44 +02:00
var solution = MopSolution ;
if ( solution ! = null )
2020-08-24 13:39:00 +02:00
{
solution . MaxVolume = value ;
}
}
2020-04-22 04:23:12 +10:00
}
2022-01-22 23:27:22 -06:00
// CurrentVolume is the volume the mop has absorbed.
2021-11-03 16:48:03 -07:00
public FixedPoint2 CurrentVolume = > MopSolution ? . CurrentVolume ? ? FixedPoint2 . Zero ;
2020-04-22 04:23:12 +10:00
2022-01-22 23:27:22 -06:00
// AvailableVolume is the remaining volume capacity of the mop.
public FixedPoint2 AvailableVolume = > MopSolution ? . AvailableVolume ? ? FixedPoint2 . Zero ;
2020-04-22 04:23:12 +10:00
// Currently there's a separate amount for pickup and dropoff so
// Picking up a puddle requires multiple clicks
// Dumping in a bucket requires 1 click
// Long-term you'd probably use a cooldown and start the pickup once we have some form of global cooldown
2021-05-04 15:37:16 +02:00
[DataField("pickup_amount")]
2021-11-24 06:14:46 +13:00
public FixedPoint2 PickupAmount { get ; } = FixedPoint2 . New ( 10 ) ;
/// <summary>
2022-02-04 20:26:11 -06:00
/// When using the mop on an empty floor tile, leave this much reagent as a new puddle.
2021-11-24 06:14:46 +13:00
/// </summary>
[DataField("residueAmount")]
2022-02-04 20:26:11 -06:00
public FixedPoint2 ResidueAmount { get ; } = FixedPoint2 . New ( 10 ) ; // Should be higher than MopLowerLimit
/// <summary>
/// To leave behind a wet floor, the mop will be unable to take from puddles with a volume less than this amount.
/// </summary>
[DataField("mopLowerLimit")]
public FixedPoint2 MopLowerLimit { get ; } = FixedPoint2 . New ( 5 ) ;
2020-04-22 04:23:12 +10:00
2021-03-05 01:08:38 +01:00
[DataField("pickup_sound")]
2021-07-10 17:35:33 +02:00
private SoundSpecifier _pickupSound = new SoundPathSpecifier ( "/Audio/Effects/Fluids/slosh.ogg" ) ;
2021-02-25 11:32:08 +11:00
/// <summary>
/// Multiplier for the do_after delay for how fast the mop works.
/// </summary>
[ViewVariables]
2021-09-06 15:49:44 +02:00
[DataField("speed")] private float _mopSpeed = 1 ;
2020-04-22 04:23:12 +10:00
2021-02-03 14:05:31 +01:00
async Task < bool > IAfterInteract . AfterInteract ( AfterInteractEventArgs eventArgs )
2020-04-22 04:23:12 +10:00
{
2021-02-25 11:32:08 +11:00
/ *
* Functionality :
* Essentially if we click on an empty tile spill our contents there
* Otherwise , try to mop up the puddle ( if it is a puddle ) .
* It will try to destroy solution on the mop to do so , and if it is successful
* will spill some of the mop ' s solution onto the puddle which will evaporate eventually .
* /
2021-11-24 06:14:46 +13:00
var solutionSystem = EntitySystem . Get < SolutionContainerSystem > ( ) ;
2021-12-05 04:18:30 +01:00
var spillableSystem = EntitySystem . Get < SpillableSystem > ( ) ;
2021-02-25 11:32:08 +11:00
2022-02-05 15:39:01 +13:00
if ( ! eventArgs . CanReach | |
! solutionSystem . TryGetSolution ( Owner , SolutionName , out var contents ) | |
Mopping )
2020-07-11 16:49:54 -05:00
{
2021-02-25 11:32:08 +11:00
return false ;
2020-07-11 16:49:54 -05:00
}
2021-12-05 21:02:04 +01:00
if ( eventArgs . Target is not { Valid : true } target )
2020-04-22 04:23:12 +10:00
{
2021-11-24 06:14:46 +13:00
// Drop the liquid on the mop on to the ground
2021-12-07 17:48:49 +01:00
var solution = solutionSystem . SplitSolution ( Owner , contents , FixedPoint2 . Min ( ResidueAmount , CurrentVolume ) ) ;
2021-12-05 04:18:30 +01:00
spillableSystem . SpillAt ( solution , eventArgs . ClickLocation , "PuddleSmear" ) ;
2021-11-24 06:14:46 +13:00
return true ;
2021-02-25 11:32:08 +11:00
}
2021-12-05 21:02:04 +01:00
if ( ! _entities . TryGetComponent ( target , out PuddleComponent ? puddleComponent ) | |
2021-12-07 22:22:34 +11:00
! solutionSystem . TryGetSolution ( ( puddleComponent ) . Owner , puddleComponent . SolutionName , out var puddleSolution ) )
2021-02-25 11:32:08 +11:00
return false ;
2022-02-04 20:26:11 -06:00
// if the puddle is too small for the mop to effectively take any more solution
if ( puddleSolution . TotalVolume < = MopLowerLimit )
{
// Transfers solution from the mop to the puddle
solutionSystem . TryAddSolution ( target , puddleSolution , solutionSystem . SplitSolution ( Owner , contents , FixedPoint2 . Min ( ResidueAmount , CurrentVolume ) ) ) ;
return true ;
}
2022-01-22 23:27:22 -06:00
// if the mop is full
if ( AvailableVolume < = 0 )
{
Owner . PopupMessage ( eventArgs . User , Loc . GetString ( "mop-component-mop-is-full-message" ) ) ;
return false ;
}
// Mopping duration (aka delay) should scale with PickupAmount and not puddle volume, because we are picking up a constant volume of solution with each click.
var doAfterArgs = new DoAfterEventArgs ( eventArgs . User , _mopSpeed * PickupAmount . Float ( ) / 10.0f ,
2021-12-05 21:02:04 +01:00
target : target )
2021-02-25 11:32:08 +11:00
{
BreakOnUserMove = true ,
BreakOnStun = true ,
BreakOnDamage = true ,
} ;
2021-11-24 06:14:46 +13:00
Mopping = true ;
2021-07-04 13:32:24 +02:00
var result = await EntitySystem . Get < DoAfterSystem > ( ) . WaitDoAfter ( doAfterArgs ) ;
2021-03-05 01:08:38 +01:00
Mopping = false ;
2021-02-25 11:32:08 +11:00
if ( result = = DoAfterStatus . Cancelled | |
2021-12-09 12:29:27 +01:00
_entities . Deleted ( Owner ) | |
2021-02-25 11:32:08 +11:00
puddleComponent . Deleted )
return false ;
2022-02-04 20:26:11 -06:00
// The volume the mop will take from the puddle
2021-11-24 06:14:46 +13:00
FixedPoint2 transferAmount ;
// does the puddle actually have reagents? it might not if its a weird cosmetic entity.
if ( puddleSolution . TotalVolume = = 0 )
2022-01-22 23:27:22 -06:00
transferAmount = FixedPoint2 . Min ( PickupAmount , AvailableVolume ) ;
2020-07-11 16:49:54 -05:00
else
2022-02-04 20:26:11 -06:00
{
2022-01-22 23:27:22 -06:00
transferAmount = FixedPoint2 . Min ( PickupAmount , puddleSolution . TotalVolume , AvailableVolume ) ;
2022-02-04 20:26:11 -06:00
if ( ( puddleSolution . TotalVolume - transferAmount ) < MopLowerLimit ) // If the transferAmount would bring the puddle below the MopLowerLimit
transferAmount = puddleSolution . TotalVolume - MopLowerLimit ; // Then the transferAmount should bring the puddle down to the MopLowerLimit exactly
}
2021-11-24 06:14:46 +13:00
2022-01-22 23:27:22 -06:00
// Transfers solution from the puddle to the mop
solutionSystem . TryAddSolution ( Owner , contents , solutionSystem . SplitSolution ( target , puddleSolution , transferAmount ) ) ;
2022-02-16 00:23:23 -07:00
2021-07-31 19:52:33 +02:00
SoundSystem . Play ( Filter . Pvs ( Owner ) , _pickupSound . GetSound ( ) , Owner ) ;
2020-04-22 04:23:12 +10:00
2022-02-04 20:26:11 -06:00
// if the mop became full after that puddle, let the player know.
if ( AvailableVolume < = 0 )
Owner . PopupMessage ( eventArgs . User , Loc . GetString ( "mop-component-mop-is-now-full-message" ) ) ;
2021-02-03 14:05:31 +01:00
return true ;
2020-04-22 04:23:12 +10:00
}
}
}