2022-11-15 12:30:59 +01:00
using Content.Server.Chemistry.Components.SolutionManager ;
2021-10-29 13:40:15 +01:00
using Content.Server.Chemistry.EntitySystems ;
2021-10-27 09:24:18 +01:00
using Content.Server.Fluids.Components ;
using Content.Shared.Examine ;
2021-11-03 16:48:03 -07:00
using Content.Shared.FixedPoint ;
2021-10-27 09:24:18 +01:00
using Content.Shared.Fluids ;
2022-11-15 12:30:59 +01:00
using Content.Shared.Slippery ;
2022-07-10 02:28:37 -07:00
using Content.Shared.StepTrigger.Components ;
using Content.Shared.StepTrigger.Systems ;
2021-10-27 09:24:18 +01:00
using JetBrains.Annotations ;
2022-11-15 12:30:59 +01:00
using Robust.Server.GameObjects ;
2021-10-27 09:24:18 +01:00
using Robust.Shared.Audio ;
2022-11-15 12:30:59 +01:00
using Robust.Shared.Map ;
2021-10-27 09:24:18 +01:00
using Robust.Shared.Player ;
2022-11-15 12:30:59 +01:00
using Solution = Content . Shared . Chemistry . Components . Solution ;
2021-10-27 09:24:18 +01:00
namespace Content.Server.Fluids.EntitySystems
{
[UsedImplicitly]
public sealed class PuddleSystem : EntitySystem
{
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default ! ;
2022-02-15 03:22:26 +01:00
[Dependency] private readonly FluidSpreaderSystem _fluidSpreaderSystem = default ! ;
2022-05-18 06:07:35 +02:00
[Dependency] private readonly StepTriggerSystem _stepTrigger = default ! ;
2022-11-15 12:30:59 +01:00
[Dependency] private readonly SlipperySystem _slipSystem = default ! ;
[Dependency] private readonly EvaporationSystem _evaporationSystem = default ! ;
2021-10-27 09:24:18 +01:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-04-24 13:54:25 +10:00
// Shouldn't need re-anchoring.
2022-01-13 06:49:28 +13:00
SubscribeLocalEvent < PuddleComponent , AnchorStateChangedEvent > ( OnAnchorChanged ) ;
2021-10-27 09:24:18 +01:00
SubscribeLocalEvent < PuddleComponent , ExaminedEvent > ( HandlePuddleExamined ) ;
SubscribeLocalEvent < PuddleComponent , SolutionChangedEvent > ( OnUpdate ) ;
SubscribeLocalEvent < PuddleComponent , ComponentInit > ( OnInit ) ;
}
private void OnInit ( EntityUid uid , PuddleComponent component , ComponentInit args )
{
2022-02-15 03:22:26 +01:00
var solution = _solutionContainerSystem . EnsureSolution ( uid , component . SolutionName ) ;
2021-11-03 16:48:03 -07:00
solution . MaxVolume = FixedPoint2 . New ( 1000 ) ;
2021-10-27 09:24:18 +01:00
}
private void OnUpdate ( EntityUid uid , PuddleComponent component , SolutionChangedEvent args )
{
UpdateSlip ( uid , component ) ;
UpdateVisuals ( uid , component ) ;
}
private void UpdateVisuals ( EntityUid uid , PuddleComponent puddleComponent )
{
2021-12-09 12:29:27 +01:00
if ( Deleted ( puddleComponent . Owner ) | | EmptyHolder ( uid , puddleComponent ) | |
2021-11-22 23:22:59 -08:00
! EntityManager . TryGetComponent < AppearanceComponent > ( uid , out var appearanceComponent ) )
2021-10-27 09:24:18 +01:00
{
return ;
}
// Opacity based on level of fullness to overflow
// Hard-cap lower bound for visibility reasons
2022-11-15 12:30:59 +01:00
var volumeScale = CurrentVolume ( puddleComponent . Owner , puddleComponent ) . Float ( ) /
puddleComponent . OverflowVolume . Float ( ) *
puddleComponent . OpacityModifier ;
2021-10-27 09:24:18 +01:00
var puddleSolution = _solutionContainerSystem . EnsureSolution ( uid , puddleComponent . SolutionName ) ;
2022-02-10 15:07:21 -06:00
2022-11-15 12:30:59 +01:00
bool hasEvaporationComponent =
EntityManager . TryGetComponent < EvaporationComponent > ( uid , out var evaporationComponent ) ;
2022-02-10 15:07:21 -06:00
bool canEvaporate = ( hasEvaporationComponent & &
2022-11-15 12:30:59 +01:00
( evaporationComponent ! . LowerLimit = = 0 | |
CurrentVolume ( puddleComponent . Owner , puddleComponent ) >
evaporationComponent . LowerLimit ) ) ;
2022-02-10 15:07:21 -06:00
2022-02-04 20:26:11 -06:00
// "Does this puddle's sprite need changing to the wet floor effect sprite?"
2022-11-15 12:30:59 +01:00
bool changeToWetFloor = ( CurrentVolume ( puddleComponent . Owner , puddleComponent ) < =
puddleComponent . WetFloorEffectThreshold
& & canEvaporate ) ;
2022-02-04 20:26:11 -06:00
2021-10-27 09:24:18 +01:00
appearanceComponent . SetData ( PuddleVisuals . VolumeScale , volumeScale ) ;
appearanceComponent . SetData ( PuddleVisuals . SolutionColor , puddleSolution . Color ) ;
2022-02-04 20:26:11 -06:00
appearanceComponent . SetData ( PuddleVisuals . ForceWetFloorSprite , changeToWetFloor ) ;
2021-10-27 09:24:18 +01:00
}
private void UpdateSlip ( EntityUid entityUid , PuddleComponent puddleComponent )
{
2021-11-03 16:48:03 -07:00
if ( ( puddleComponent . SlipThreshold = = FixedPoint2 . New ( - 1 ) | |
2022-11-15 12:30:59 +01:00
CurrentVolume ( puddleComponent . Owner , puddleComponent ) < puddleComponent . SlipThreshold ) & &
2022-05-18 06:07:35 +02:00
TryComp ( entityUid , out StepTriggerComponent ? stepTrigger ) )
2021-10-27 09:24:18 +01:00
{
2022-05-18 06:07:35 +02:00
_stepTrigger . SetActive ( entityUid , false , stepTrigger ) ;
2021-10-27 09:24:18 +01:00
}
2022-11-15 12:30:59 +01:00
else if ( CurrentVolume ( puddleComponent . Owner , puddleComponent ) > = puddleComponent . SlipThreshold )
2021-10-27 09:24:18 +01:00
{
2022-05-18 06:07:35 +02:00
var comp = EnsureComp < StepTriggerComponent > ( entityUid ) ;
_stepTrigger . SetActive ( entityUid , true , comp ) ;
2021-10-27 09:24:18 +01:00
}
}
private void HandlePuddleExamined ( EntityUid uid , PuddleComponent component , ExaminedEvent args )
{
2022-05-18 06:07:35 +02:00
if ( TryComp < StepTriggerComponent > ( uid , out var slippery ) & & slippery . Active )
2021-10-27 09:24:18 +01:00
{
args . PushText ( Loc . GetString ( "puddle-component-examine-is-slipper-text" ) ) ;
}
}
2022-01-13 06:49:28 +13:00
private void OnAnchorChanged ( EntityUid uid , PuddleComponent puddle , ref AnchorStateChangedEvent args )
2021-10-27 09:24:18 +01:00
{
2022-01-13 06:49:28 +13:00
if ( ! args . Anchored )
QueueDel ( uid ) ;
2021-10-27 09:24:18 +01:00
}
public bool EmptyHolder ( EntityUid uid , PuddleComponent ? puddleComponent = null )
{
if ( ! Resolve ( uid , ref puddleComponent ) )
return true ;
2021-12-03 15:53:09 +01:00
return ! _solutionContainerSystem . TryGetSolution ( puddleComponent . Owner , puddleComponent . SolutionName ,
2021-10-27 09:24:18 +01:00
out var solution )
| | solution . Contents . Count = = 0 ;
}
2021-11-03 16:48:03 -07:00
public FixedPoint2 CurrentVolume ( EntityUid uid , PuddleComponent ? puddleComponent = null )
2021-10-27 09:24:18 +01:00
{
if ( ! Resolve ( uid , ref puddleComponent ) )
2021-11-03 16:48:03 -07:00
return FixedPoint2 . Zero ;
2021-10-27 09:24:18 +01:00
2021-12-03 15:53:09 +01:00
return _solutionContainerSystem . TryGetSolution ( puddleComponent . Owner , puddleComponent . SolutionName ,
2021-10-27 09:24:18 +01:00
out var solution )
? solution . CurrentVolume
2021-11-03 16:48:03 -07:00
: FixedPoint2 . Zero ;
2021-10-27 09:24:18 +01:00
}
2022-02-15 03:22:26 +01:00
/// <summary>
2022-11-15 12:30:59 +01:00
/// Try to add solution to <paramref name="puddleUid"/>.
2022-02-15 03:22:26 +01:00
/// </summary>
/// <param name="puddleUid">Puddle to which we add</param>
/// <param name="addedSolution">Solution that is added to puddleComponent</param>
/// <param name="sound">Play sound on overflow</param>
/// <param name="checkForOverflow">Overflow on encountered values</param>
/// <param name="puddleComponent">Optional resolved PuddleComponent</param>
/// <returns></returns>
public bool TryAddSolution ( EntityUid puddleUid ,
Solution addedSolution ,
2021-10-27 09:24:18 +01:00
bool sound = true ,
bool checkForOverflow = true ,
PuddleComponent ? puddleComponent = null )
{
2022-02-15 03:22:26 +01:00
if ( ! Resolve ( puddleUid , ref puddleComponent ) )
2021-10-27 09:24:18 +01:00
return false ;
2022-02-15 03:22:26 +01:00
if ( addedSolution . TotalVolume = = 0 | |
2021-12-03 15:53:09 +01:00
! _solutionContainerSystem . TryGetSolution ( puddleComponent . Owner , puddleComponent . SolutionName ,
2022-11-15 12:30:59 +01:00
out var solution ) )
2021-10-27 09:24:18 +01:00
{
return false ;
}
2022-11-15 12:30:59 +01:00
solution . AddSolution ( addedSolution ) ;
if ( checkForOverflow & & IsOverflowing ( puddleUid , puddleComponent ) )
2021-10-27 09:24:18 +01:00
{
2022-11-15 12:30:59 +01:00
_fluidSpreaderSystem . AddOverflowingPuddle ( puddleComponent . Owner , puddleComponent ) ;
2021-10-27 09:24:18 +01:00
}
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( puddleComponent . Owner , new SolutionChangedEvent ( ) , true ) ;
2021-10-27 09:24:18 +01:00
if ( ! sound )
{
return true ;
}
2022-06-12 19:45:47 -04:00
SoundSystem . Play ( puddleComponent . SpillSound . GetSound ( ) ,
Filter . Pvs ( puddleComponent . Owner ) , puddleComponent . Owner ) ;
2021-10-27 09:24:18 +01:00
return true ;
}
2022-11-15 12:30:59 +01:00
/// <summary>
/// Given a large srcPuddle and smaller destination puddles, this method will equalize their <see cref="Solution.CurrentVolume"/>
/// </summary>
/// <param name="srcPuddle">puddle that donates liquids to other puddles</param>
/// <param name="destinationPuddles">List of puddles that we want to equalize, their puddle <see cref="Solution.CurrentVolume"/> should be less than sourcePuddleComponent</param>
/// <param name="totalVolume">Total volume of src and destination puddle</param>
/// <param name="stillOverflowing">optional parameter, that after equalization adds all still overflowing puddles.</param>
/// <param name="sourcePuddleComponent">puddleComponent for <paramref name="srcPuddle"/></param>
public void EqualizePuddles ( EntityUid srcPuddle , List < PuddleComponent > destinationPuddles ,
FixedPoint2 totalVolume ,
HashSet < EntityUid > ? stillOverflowing = null ,
PuddleComponent ? sourcePuddleComponent = null )
{
if ( ! Resolve ( srcPuddle , ref sourcePuddleComponent )
| | ! _solutionContainerSystem . TryGetSolution ( srcPuddle , sourcePuddleComponent . SolutionName ,
out var srcSolution ) )
return ;
var dividedVolume = totalVolume / ( destinationPuddles . Count + 1 ) ;
foreach ( var destPuddle in destinationPuddles )
{
if ( ! _solutionContainerSystem . TryGetSolution ( destPuddle . Owner , destPuddle . SolutionName ,
out var destSolution ) )
continue ;
var takeAmount = FixedPoint2 . Max ( 0 , dividedVolume - destSolution . CurrentVolume ) ;
TryAddSolution ( destPuddle . Owner , srcSolution . SplitSolution ( takeAmount ) , false , false , destPuddle ) ;
if ( stillOverflowing ! = null & & IsOverflowing ( destPuddle . Owner , destPuddle ) )
{
stillOverflowing . Add ( destPuddle . Owner ) ;
}
}
if ( stillOverflowing ! = null & & srcSolution . CurrentVolume > sourcePuddleComponent . OverflowVolume )
{
stillOverflowing . Add ( srcPuddle ) ;
}
}
2021-10-27 09:24:18 +01:00
/// <summary>
2022-02-15 03:22:26 +01:00
/// Whether adding this solution to this puddle would overflow.
2021-10-27 09:24:18 +01:00
/// </summary>
2022-02-15 03:22:26 +01:00
/// <param name="uid">Uid of owning entity</param>
/// <param name="puddle">Puddle to which we are adding solution</param>
/// <param name="solution">Solution we intend to add</param>
/// <returns></returns>
public bool WouldOverflow ( EntityUid uid , Solution solution , PuddleComponent ? puddle = null )
2021-10-27 09:24:18 +01:00
{
2022-02-15 03:22:26 +01:00
if ( ! Resolve ( uid , ref puddle ) )
2021-10-27 09:24:18 +01:00
return false ;
2022-11-15 12:30:59 +01:00
return CurrentVolume ( uid , puddle ) + solution . TotalVolume > puddle . OverflowVolume ;
}
/// <summary>
/// Whether adding this solution to this puddle would overflow.
/// </summary>
/// <param name="uid">Uid of owning entity</param>
/// <param name="puddle">Puddle ref param</param>
/// <returns></returns>
private bool IsOverflowing ( EntityUid uid , PuddleComponent ? puddle = null )
{
if ( ! Resolve ( uid , ref puddle ) )
return false ;
return CurrentVolume ( uid , puddle ) > puddle . OverflowVolume ;
}
public PuddleComponent SpawnPuddle ( EntityUid srcUid , EntityCoordinates pos , PuddleComponent ? srcPuddleComponent = null )
{
MetaDataComponent ? metadata = null ;
Resolve ( srcUid , ref srcPuddleComponent , ref metadata ) ;
var prototype = metadata ? . EntityPrototype ? . ID ? ? "PuddleSmear" ; // TODO Spawn a entity based on another entity
var destUid = EntityManager . SpawnEntity ( prototype , pos ) ;
var destPuddle = EntityManager . EnsureComponent < PuddleComponent > ( destUid ) ;
return destPuddle ;
2021-10-27 09:24:18 +01:00
}
}
}