2021-08-12 13:40:38 +10:00
using System.Linq ;
using System.Threading ;
2023-01-04 00:45:35 -06:00
using Content.Server.Administration.Logs ;
2021-08-12 13:40:38 +10:00
using Content.Server.Atmos.EntitySystems ;
using Content.Server.Disposal.Tube.Components ;
2021-10-20 21:12:23 +02:00
using Content.Server.Disposal.Unit.Components ;
using Content.Server.DoAfter ;
2021-08-12 13:40:38 +10:00
using Content.Server.Hands.Components ;
2022-07-31 01:17:30 -03:00
using Content.Server.Popups ;
2021-08-12 13:40:38 +10:00
using Content.Server.Power.Components ;
using Content.Shared.ActionBlocker ;
using Content.Shared.Atmos ;
2022-04-02 16:52:44 +13:00
using Content.Shared.Construction.Components ;
2023-01-04 00:45:35 -06:00
using Content.Shared.Database ;
2022-05-03 01:43:25 +03:00
using Content.Shared.Destructible ;
2021-08-12 13:40:38 +10:00
using Content.Shared.Disposal ;
using Content.Shared.Disposal.Components ;
2022-01-30 17:53:22 +01:00
using Content.Shared.DragDrop ;
2022-07-31 01:17:30 -03:00
using Content.Shared.Hands.Components ;
2022-03-17 20:13:31 +13:00
using Content.Shared.Hands.EntitySystems ;
2021-08-12 13:40:38 +10:00
using Content.Shared.Interaction ;
2021-12-30 22:56:10 +01:00
using Content.Shared.Item ;
2022-06-24 17:44:30 +10:00
using Content.Shared.Movement.Events ;
2022-07-31 01:17:30 -03:00
using Content.Shared.Popups ;
2021-08-12 13:40:38 +10:00
using Content.Shared.Throwing ;
2021-10-20 21:12:23 +02:00
using Content.Shared.Verbs ;
2021-08-12 13:40:38 +10:00
using Robust.Server.GameObjects ;
2022-01-30 17:53:22 +01:00
using Robust.Shared.Audio ;
2021-08-12 13:40:38 +10:00
using Robust.Shared.Containers ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Map ;
2022-11-22 13:12:04 +11:00
using Robust.Shared.Map.Components ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Components ;
2022-01-30 17:53:22 +01:00
using Robust.Shared.Player ;
2021-08-12 13:40:38 +10:00
using Robust.Shared.Random ;
2021-05-13 02:05:46 +02:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Disposal.Unit.EntitySystems
2021-05-13 02:05:46 +02:00
{
2021-08-12 13:40:38 +10:00
public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
2021-05-13 02:05:46 +02:00
{
2021-08-12 13:40:38 +10:00
[Dependency] private readonly IRobustRandom _robustRandom = default ! ;
2023-01-04 00:45:35 -06:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2021-10-05 14:29:03 +11:00
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default ! ;
2022-09-11 16:50:59 +10:00
[Dependency] private readonly AppearanceSystem _appearance = default ! ;
2021-08-12 13:40:38 +10:00
[Dependency] private readonly AtmosphereSystem _atmosSystem = default ! ;
2022-09-11 16:50:59 +10:00
[Dependency] private readonly AudioSystem _audio = default ! ;
2021-10-20 21:12:23 +02:00
[Dependency] private readonly DoAfterSystem _doAfterSystem = default ! ;
2022-09-11 16:50:59 +10:00
[Dependency] private readonly EntityLookupSystem _lookup = default ! ;
2022-07-31 01:17:30 -03:00
[Dependency] private readonly PopupSystem _popupSystem = default ! ;
2022-09-11 16:50:59 +10:00
[Dependency] private readonly SharedContainerSystem _containerSystem = default ! ;
2022-03-17 20:13:31 +13:00
[Dependency] private readonly SharedHandsSystem _handsSystem = default ! ;
2022-07-04 16:51:34 +02:00
[Dependency] private readonly TransformSystem _transformSystem = default ! ;
2022-09-11 16:50:59 +10:00
[Dependency] private readonly UserInterfaceSystem _ui = default ! ;
2021-08-12 13:40:38 +10:00
2021-05-13 02:05:46 +02: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 < DisposalUnitComponent , AnchorStateChangedEvent > ( OnAnchorChanged ) ;
2021-08-12 13:40:38 +10:00
// TODO: Predict me when hands predicted
2022-07-16 13:51:52 +10:00
SubscribeLocalEvent < DisposalUnitComponent , ContainerRelayMovementEntityEvent > ( HandleMovement ) ;
2021-08-12 13:40:38 +10:00
SubscribeLocalEvent < DisposalUnitComponent , PowerChangedEvent > ( HandlePowerChange ) ;
// Component lifetime
SubscribeLocalEvent < DisposalUnitComponent , ComponentInit > ( HandleDisposalInit ) ;
2022-01-13 06:49:28 +13:00
SubscribeLocalEvent < DisposalUnitComponent , ComponentRemove > ( HandleDisposalRemove ) ;
2021-08-12 13:40:38 +10:00
2021-08-21 09:18:23 +02:00
SubscribeLocalEvent < DisposalUnitComponent , ThrowHitByEvent > ( HandleThrowCollide ) ;
2021-08-12 13:40:38 +10:00
// Interactions
SubscribeLocalEvent < DisposalUnitComponent , ActivateInWorldEvent > ( HandleActivate ) ;
2022-02-05 15:39:01 +13:00
SubscribeLocalEvent < DisposalUnitComponent , AfterInteractUsingEvent > ( HandleAfterInteractUsing ) ;
2022-01-30 17:53:22 +01:00
SubscribeLocalEvent < DisposalUnitComponent , DragDropEvent > ( HandleDragDropOn ) ;
SubscribeLocalEvent < DisposalUnitComponent , DestructionEventArgs > ( HandleDestruction ) ;
2021-10-05 14:29:03 +11:00
// Verbs
2022-02-10 15:30:59 +13:00
SubscribeLocalEvent < DisposalUnitComponent , GetVerbsEvent < InteractionVerb > > ( AddInsertVerb ) ;
2022-06-27 05:19:18 +01:00
SubscribeLocalEvent < DisposalUnitComponent , GetVerbsEvent < AlternativeVerb > > ( AddDisposalAltVerbs ) ;
2022-02-10 15:30:59 +13:00
SubscribeLocalEvent < DisposalUnitComponent , GetVerbsEvent < Verb > > ( AddClimbInsideVerb ) ;
2021-10-20 21:12:23 +02:00
// Units
SubscribeLocalEvent < DoInsertDisposalUnitEvent > ( DoInsertDisposalUnit ) ;
2022-01-30 17:53:22 +01:00
//UI
SubscribeLocalEvent < DisposalUnitComponent , SharedDisposalUnitComponent . UiButtonPressedMessage > ( OnUiButtonPressed ) ;
2021-10-05 14:29:03 +11:00
}
2021-10-20 21:12:23 +02:00
2022-06-27 05:19:18 +01:00
private void AddDisposalAltVerbs ( EntityUid uid , DisposalUnitComponent component , GetVerbsEvent < AlternativeVerb > args )
2021-10-05 14:29:03 +11:00
{
2022-06-27 05:19:18 +01:00
if ( ! args . CanAccess | | ! args . CanInteract )
2021-10-05 14:29:03 +11:00
return ;
2022-06-27 05:19:18 +01:00
// Behavior for if the disposals bin has items in it
if ( component . Container . ContainedEntities . Count > 0 )
{
// Verbs to flush the unit
AlternativeVerb flushVerb = new ( ) ;
flushVerb . Act = ( ) = > Engage ( component ) ;
flushVerb . Text = Loc . GetString ( "disposal-flush-verb-get-data-text" ) ;
flushVerb . IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png" ;
flushVerb . Priority = 1 ;
args . Verbs . Add ( flushVerb ) ;
// Verb to eject the contents
AlternativeVerb ejectVerb = new ( )
{
Act = ( ) = > TryEjectContents ( component ) ,
Category = VerbCategory . Eject ,
2022-08-15 15:58:58 +12:00
Text = Loc . GetString ( "disposal-eject-verb-get-data-text" )
2022-06-27 05:19:18 +01:00
} ;
args . Verbs . Add ( ejectVerb ) ;
}
2021-10-05 14:29:03 +11:00
}
2022-02-10 15:30:59 +13:00
private void AddClimbInsideVerb ( EntityUid uid , DisposalUnitComponent component , GetVerbsEvent < Verb > args )
2021-10-05 14:29:03 +11:00
{
// This is not an interaction, activation, or alternative verb type because unfortunately most users are
// unwilling to accept that this is where they belong and don't want to accidentally climb inside.
2022-08-14 07:57:25 +02:00
if ( ! component . MobsCanEnter | |
! args . CanAccess | |
2021-10-05 14:29:03 +11:00
! args . CanInteract | |
2022-01-30 17:53:22 +01:00
component . Container . ContainedEntities . Contains ( args . User ) | |
2021-12-03 15:53:09 +01:00
! _actionBlockerSystem . CanMove ( args . User ) )
2021-10-05 14:29:03 +11:00
return ;
2021-10-20 21:12:23 +02:00
// Add verb to climb inside of the unit,
Verb verb = new ( )
{
2021-12-03 15:53:09 +01:00
Act = ( ) = > TryInsert ( component . Owner , args . User , args . User ) ,
2022-10-26 14:15:48 +13:00
DoContactInteraction = true ,
2021-10-20 21:12:23 +02:00
Text = Loc . GetString ( "disposal-self-insert-verb-get-data-text" )
} ;
2021-10-05 14:29:03 +11:00
// TODO VERN ICON
// TODO VERB CATEGORY
// create a verb category for "enter"?
// See also, medical scanner. Also maybe add verbs for entering lockers/body bags?
args . Verbs . Add ( verb ) ;
2021-08-12 13:40:38 +10:00
}
2022-02-10 15:30:59 +13:00
private void AddInsertVerb ( EntityUid uid , DisposalUnitComponent component , GetVerbsEvent < InteractionVerb > args )
2022-02-07 14:54:54 +13:00
{
if ( ! args . CanAccess | | ! args . CanInteract | | args . Hands = = null | | args . Using = = null )
return ;
if ( ! _actionBlockerSystem . CanDrop ( args . User ) )
return ;
if ( ! CanInsert ( component , args . Using . Value ) )
return ;
2022-02-10 15:30:59 +13:00
InteractionVerb insertVerb = new ( )
2022-02-07 14:54:54 +13:00
{
Text = Name ( args . Using . Value ) ,
Category = VerbCategory . Insert ,
Act = ( ) = >
{
2022-03-17 20:13:31 +13:00
_handsSystem . TryDropIntoContainer ( args . User , args . Using . Value , component . Container , checkActionBlocker : false , args . Hands ) ;
2023-01-04 00:45:35 -06:00
_adminLogger . Add ( LogType . Action , LogImpact . Medium , $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}" ) ;
2022-02-07 14:54:54 +13:00
AfterInsert ( component , args . Using . Value ) ;
}
} ;
args . Verbs . Add ( insertVerb ) ;
}
2021-10-20 21:12:23 +02:00
private void DoInsertDisposalUnit ( DoInsertDisposalUnitEvent ev )
{
2021-12-05 21:02:04 +01:00
var toInsert = ev . ToInsert ;
2021-10-20 21:12:23 +02:00
if ( ! EntityManager . TryGetComponent ( ev . Unit , out DisposalUnitComponent ? unit ) )
{
return ;
}
2023-01-04 00:45:35 -06:00
if ( ! unit . Container . Insert ( toInsert ) )
return ;
if ( ev . User ! = null )
_adminLogger . Add ( LogType . Action , LogImpact . Medium ,
$"{ToPrettyString(ev.User.Value):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(unit.Owner)}" ) ;
AfterInsert ( unit , toInsert ) ;
2021-10-20 21:12:23 +02:00
}
2023-01-04 00:45:35 -06:00
public void DoInsertDisposalUnit ( EntityUid unit , EntityUid toInsert , EntityUid user , DisposalUnitComponent ? disposal = null )
2022-05-03 23:00:22 -04:00
{
if ( ! Resolve ( unit , ref disposal ) )
return ;
if ( ! disposal . Container . Insert ( toInsert ) )
return ;
2023-01-04 00:45:35 -06:00
_adminLogger . Add ( LogType . Action , LogImpact . Medium , $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(unit)}" ) ;
2022-05-03 23:00:22 -04:00
AfterInsert ( disposal , toInsert ) ;
}
2021-08-12 13:40:38 +10:00
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
2022-09-11 16:50:59 +10:00
foreach ( var ( _ , comp ) in EntityQuery < ActiveDisposalUnitComponent , DisposalUnitComponent > ( ) )
2021-08-12 13:40:38 +10:00
{
2022-09-11 16:50:59 +10:00
if ( ! Update ( comp , frameTime ) )
continue ;
RemComp < ActiveDisposalUnitComponent > ( comp . Owner ) ;
2021-08-12 13:40:38 +10:00
}
}
#region UI Handlers
2022-01-30 17:53:22 +01:00
private void OnUiButtonPressed ( EntityUid uid , DisposalUnitComponent component , SharedDisposalUnitComponent . UiButtonPressedMessage args )
{
if ( args . Session . AttachedEntity is not { Valid : true } player )
{
return ;
}
switch ( args . Button )
{
case SharedDisposalUnitComponent . UiButton . Eject :
TryEjectContents ( component ) ;
2023-01-04 00:45:35 -06:00
_adminLogger . Add ( LogType . Action , LogImpact . Low , $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}" ) ;
2022-01-30 17:53:22 +01:00
break ;
case SharedDisposalUnitComponent . UiButton . Engage :
ToggleEngage ( component ) ;
2023-01-04 00:45:35 -06:00
_adminLogger . Add ( LogType . Action , LogImpact . Low , $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? " on " : " off ")}" ) ;
2022-01-30 17:53:22 +01:00
break ;
case SharedDisposalUnitComponent . UiButton . Power :
TogglePower ( component ) ;
2022-09-11 16:50:59 +10:00
_audio . PlayPvs ( new SoundPathSpecifier ( "/Audio/Machines/machine_switch.ogg" ) , component . Owner ,
AudioParams . Default . WithVolume ( - 2f ) ) ;
2023-01-04 00:45:35 -06:00
if ( EntityManager . TryGetComponent ( component . Owner , out ApcPowerReceiverComponent ? receiver ) )
{
_adminLogger . Add ( LogType . Action , LogImpact . Low , $"{ToPrettyString(player):player} hit power button on {ToPrettyString(uid)}, it's now {(!receiver.PowerDisabled ? " on " : " off ")}" ) ;
}
2022-01-30 17:53:22 +01:00
break ;
default :
throw new ArgumentOutOfRangeException ( ) ;
}
}
2021-08-12 13:40:38 +10:00
public void ToggleEngage ( DisposalUnitComponent component )
{
component . Engaged ^ = true ;
if ( component . Engaged )
{
Engage ( component ) ;
}
else
{
Disengage ( component ) ;
}
}
public void TogglePower ( DisposalUnitComponent component )
{
2021-12-03 15:53:09 +01:00
if ( ! EntityManager . TryGetComponent ( component . Owner , out ApcPowerReceiverComponent ? receiver ) )
2021-08-12 13:40:38 +10:00
{
return ;
}
receiver . PowerDisabled = ! receiver . PowerDisabled ;
UpdateInterface ( component , receiver . Powered ) ;
}
#endregion
#region Eventbus Handlers
private void HandleActivate ( EntityUid uid , DisposalUnitComponent component , ActivateInWorldEvent args )
{
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . TryGetComponent ( args . User , out ActorComponent ? actor ) )
2021-08-12 13:40:38 +10:00
{
return ;
}
args . Handled = true ;
2022-09-11 16:50:59 +10:00
_ui . TryOpen ( component . Owner , SharedDisposalUnitComponent . DisposalUnitUiKey . Key , actor . PlayerSession ) ;
2021-08-12 13:40:38 +10:00
}
2022-02-05 15:39:01 +13:00
private void HandleAfterInteractUsing ( EntityUid uid , DisposalUnitComponent component , AfterInteractUsingEvent args )
2021-08-12 13:40:38 +10:00
{
2022-02-05 15:39:01 +13:00
if ( args . Handled | | ! args . CanReach )
return ;
2021-08-12 13:40:38 +10:00
2022-09-11 16:50:59 +10:00
if ( ! EntityManager . HasComponent < HandsComponent > ( args . User ) )
2021-08-12 13:40:38 +10:00
{
return ;
}
2022-04-24 13:54:25 +10:00
2022-03-17 20:13:31 +13:00
if ( ! CanInsert ( component , args . Used ) | | ! _handsSystem . TryDropIntoContainer ( args . User , args . Used , component . Container ) )
2021-08-12 13:40:38 +10:00
{
return ;
}
2023-01-04 00:45:35 -06:00
_adminLogger . Add ( LogType . Action , LogImpact . Medium , $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}" ) ;
2021-08-12 13:40:38 +10:00
AfterInsert ( component , args . Used ) ;
args . Handled = true ;
}
/// <summary>
/// Thrown items have a chance of bouncing off the unit and not going in.
/// </summary>
2021-08-21 09:18:23 +02:00
private void HandleThrowCollide ( EntityUid uid , DisposalUnitComponent component , ThrowHitByEvent args )
2021-08-12 13:40:38 +10:00
{
if ( ! CanInsert ( component , args . Thrown ) | |
_robustRandom . NextDouble ( ) > 0.75 | |
! component . Container . Insert ( args . Thrown ) )
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( "disposal-unit-thrown-missed" ) , uid ) ;
2021-08-12 13:40:38 +10:00
return ;
}
2023-01-04 00:45:35 -06:00
if ( args . User ! = null )
_adminLogger . Add ( LogType . Landed , LogImpact . Low , $"{ToPrettyString(args.Thrown)} thrown by {ToPrettyString(args.User.Value):player} landed in {ToPrettyString(uid)}" ) ;
2021-08-12 13:40:38 +10:00
AfterInsert ( component , args . Thrown ) ;
}
private void HandleDisposalInit ( EntityUid uid , DisposalUnitComponent component , ComponentInit args )
{
2022-08-17 12:47:58 +12:00
component . Container = _containerSystem . EnsureContainer < Container > ( uid , SharedDisposalUnitComponent . ContainerId ) ;
2021-08-12 13:40:38 +10:00
UpdateInterface ( component , component . Powered ) ;
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . HasComponent < AnchorableComponent > ( component . Owner ) )
2021-08-12 13:40:38 +10:00
{
Logger . WarningS ( "VitalComponentMissing" , $"Disposal unit {uid} is missing an {nameof(AnchorableComponent)}" ) ;
}
2021-05-13 02:05:46 +02:00
}
2022-01-13 06:49:28 +13:00
private void HandleDisposalRemove ( EntityUid uid , DisposalUnitComponent component , ComponentRemove args )
2021-05-13 02:05:46 +02:00
{
2021-08-12 13:40:38 +10:00
foreach ( var entity in component . Container . ContainedEntities . ToArray ( ) )
{
component . Container . ForceRemove ( entity ) ;
}
2022-09-11 16:50:59 +10:00
_ui . TryCloseAll ( component . Owner , SharedDisposalUnitComponent . DisposalUnitUiKey . Key ) ;
2021-08-12 13:40:38 +10:00
component . AutomaticEngageToken ? . Cancel ( ) ;
component . AutomaticEngageToken = null ;
component . Container = null ! ;
2022-09-11 16:50:59 +10:00
RemComp < ActiveDisposalUnitComponent > ( uid ) ;
2021-05-13 02:05:46 +02:00
}
2021-07-16 05:22:29 +02:00
2022-10-15 15:08:15 +11:00
private void HandlePowerChange ( EntityUid uid , DisposalUnitComponent component , ref PowerChangedEvent args )
2021-07-16 05:22:29 +02:00
{
2021-12-03 10:25:07 +01:00
if ( ! component . Running )
return ;
2022-01-30 17:53:22 +01:00
component . Powered = args . Powered ;
2021-08-12 13:40:38 +10:00
// TODO: Need to check the other stuff.
if ( ! args . Powered )
{
component . AutomaticEngageToken ? . Cancel ( ) ;
component . AutomaticEngageToken = null ;
}
HandleStateChange ( component , args . Powered & & component . State = = SharedDisposalUnitComponent . PressureState . Pressurizing ) ;
UpdateVisualState ( component ) ;
UpdateInterface ( component , args . Powered ) ;
if ( component . Engaged & & ! TryFlush ( component ) )
{
TryQueueEngage ( component ) ;
}
}
/// <summary>
/// Add or remove this disposal from the active ones for updating.
/// </summary>
public void HandleStateChange ( DisposalUnitComponent component , bool active )
{
if ( active )
{
2022-09-11 16:50:59 +10:00
EnsureComp < ActiveDisposalUnitComponent > ( component . Owner ) ;
2021-08-12 13:40:38 +10:00
}
else
{
2022-09-11 16:50:59 +10:00
RemComp < ActiveDisposalUnitComponent > ( component . Owner ) ;
2021-08-12 13:40:38 +10:00
}
}
2022-07-16 13:51:52 +10:00
private void HandleMovement ( EntityUid uid , DisposalUnitComponent component , ref ContainerRelayMovementEntityEvent args )
2021-08-12 13:40:38 +10:00
{
var currentTime = GameTiming . CurTime ;
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . TryGetComponent ( args . Entity , out HandsComponent ? hands ) | |
2021-08-12 13:40:38 +10:00
hands . Count = = 0 | |
currentTime < component . LastExitAttempt + ExitAttemptDelay )
{
return ;
}
component . LastExitAttempt = currentTime ;
Remove ( component , args . Entity ) ;
}
2022-01-13 06:49:28 +13:00
private void OnAnchorChanged ( EntityUid uid , DisposalUnitComponent component , ref AnchorStateChangedEvent args )
2021-08-12 13:40:38 +10:00
{
2022-10-29 10:59:01 +13:00
if ( Terminating ( uid ) )
return ;
2021-08-12 13:40:38 +10:00
UpdateVisualState ( component ) ;
2022-01-13 06:49:28 +13:00
if ( ! args . Anchored )
TryEjectContents ( component ) ;
2021-08-12 13:40:38 +10:00
}
2022-01-30 17:53:22 +01:00
private void HandleDestruction ( EntityUid uid , DisposalUnitComponent component , DestructionEventArgs args )
{
TryEjectContents ( component ) ;
}
private void HandleDragDropOn ( EntityUid uid , DisposalUnitComponent component , DragDropEvent args )
{
args . Handled = TryInsert ( component . Owner , args . Dragged , args . User ) ;
}
2021-08-12 13:40:38 +10:00
#endregion
/// <summary>
/// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding.
/// </summary>
private bool Update ( DisposalUnitComponent component , float frameTime )
{
var oldPressure = component . Pressure ;
component . Pressure = MathF . Min ( 1.0f , component . Pressure + PressurePerSecond * frameTime ) ;
2022-01-30 17:53:22 +01:00
component . State = component . Pressure > = 1 ? SharedDisposalUnitComponent . PressureState . Ready : SharedDisposalUnitComponent . PressureState . Pressurizing ;
2021-08-12 13:40:38 +10:00
var state = component . State ;
if ( oldPressure < 1 & & state = = SharedDisposalUnitComponent . PressureState . Ready )
{
UpdateVisualState ( component ) ;
2022-08-14 07:57:25 +02:00
UpdateInterface ( component , component . Powered ) ;
2021-08-12 13:40:38 +10:00
if ( component . Engaged )
{
TryFlush ( component ) ;
2022-06-23 21:26:20 -04:00
state = component . State ;
2021-08-12 13:40:38 +10:00
}
}
Box2 ? disposalsBounds = null ;
var count = component . RecentlyEjected . Count ;
if ( count > 0 )
{
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . TryGetComponent ( component . Owner , out PhysicsComponent ? disposalsBody ) )
2021-08-12 13:40:38 +10:00
{
component . RecentlyEjected . Clear ( ) ;
}
else
{
2022-09-11 16:50:59 +10:00
disposalsBounds = _lookup . GetWorldAABB ( disposalsBody . Owner ) ;
2021-08-12 13:40:38 +10:00
}
}
for ( var i = component . RecentlyEjected . Count - 1 ; i > = 0 ; i - - )
{
var uid = component . RecentlyEjected [ i ] ;
if ( EntityManager . EntityExists ( uid ) & &
2021-09-28 13:35:29 +02:00
EntityManager . TryGetComponent ( uid , out PhysicsComponent ? body ) )
2021-08-12 13:40:38 +10:00
{
// TODO: We need to use a specific collision method (which sloth hasn't coded yet) for actual bounds overlaps.
// Check for itemcomp as we won't just block the disposal unit "sleeping" for something it can't collide with anyway.
2022-09-11 16:50:59 +10:00
if ( ! EntityManager . HasComponent < ItemComponent > ( uid ) & & _lookup . GetWorldAABB ( body . Owner ) . Intersects ( disposalsBounds ! . Value ) ) continue ;
2021-08-12 13:40:38 +10:00
component . RecentlyEjected . RemoveAt ( i ) ;
}
}
if ( count ! = component . RecentlyEjected . Count )
2022-02-02 17:34:25 +11:00
Dirty ( component ) ;
2021-08-12 13:40:38 +10:00
return state = = SharedDisposalUnitComponent . PressureState . Ready & & component . RecentlyEjected . Count = = 0 ;
}
2022-07-31 14:56:26 +12:00
public bool TryInsert ( EntityUid unitId , EntityUid toInsertId , EntityUid ? userId , DisposalUnitComponent ? unit = null )
2021-10-20 21:12:23 +02:00
{
if ( ! Resolve ( unitId , ref unit ) )
2022-01-30 17:53:22 +01:00
return false ;
2021-10-20 21:12:23 +02:00
2022-07-30 21:23:46 -07:00
if ( userId . HasValue & & ! HasComp < SharedHandsComponent > ( userId ) & & toInsertId ! = userId ) // Mobs like mouse can Jump inside even with no hands
2022-07-31 01:17:30 -03:00
{
2022-12-19 10:41:47 +13:00
_popupSystem . PopupEntity ( Loc . GetString ( "disposal-unit-no-hands" ) , userId . Value , userId . Value , PopupType . SmallCaution ) ;
2022-07-31 01:17:30 -03:00
return false ;
}
2021-10-20 21:12:23 +02:00
if ( ! CanInsert ( unit , toInsertId ) )
2022-01-30 17:53:22 +01:00
return false ;
2021-10-20 21:12:23 +02:00
var delay = userId = = toInsertId ? unit . EntryDelay : unit . DraggedEntryDelay ;
var ev = new DoInsertDisposalUnitEvent ( userId , toInsertId , unitId ) ;
2022-07-31 14:56:26 +12:00
if ( delay < = 0 | | userId = = null )
2021-10-20 21:12:23 +02:00
{
DoInsertDisposalUnit ( ev ) ;
2022-01-30 17:53:22 +01:00
return true ;
2021-10-20 21:12:23 +02:00
}
// Can't check if our target AND disposals moves currently so we'll just check target.
// if you really want to check if disposals moves then add a predicate.
2022-07-31 14:56:26 +12:00
var doAfterArgs = new DoAfterEventArgs ( userId . Value , delay , default , toInsertId )
2021-10-20 21:12:23 +02:00
{
BreakOnDamage = true ,
BreakOnStun = true ,
BreakOnTargetMove = true ,
BreakOnUserMove = true ,
NeedHand = false ,
BroadcastFinishedEvent = ev
} ;
_doAfterSystem . DoAfter ( doAfterArgs ) ;
2022-01-30 17:53:22 +01:00
return true ;
2021-10-20 21:12:23 +02:00
}
2021-08-12 13:40:38 +10:00
public bool TryFlush ( DisposalUnitComponent component )
{
if ( component . Deleted | | ! CanFlush ( component ) )
{
return false ;
}
2022-08-14 07:57:25 +02:00
//Allows the MailingUnitSystem to add tags or prevent flushing
var beforeFlushArgs = new BeforeDisposalFlushEvent ( ) ;
2022-09-11 16:50:59 +10:00
RaiseLocalEvent ( component . Owner , beforeFlushArgs ) ;
2022-08-14 07:57:25 +02:00
if ( beforeFlushArgs . Cancelled )
{
Disengage ( component ) ;
return false ;
}
2022-06-20 12:14:35 +12:00
var xform = Transform ( component . Owner ) ;
2022-11-04 10:12:25 +11:00
if ( ! TryComp ( xform . GridUid , out MapGridComponent ? grid ) )
2022-06-20 12:14:35 +12:00
return false ;
var coords = xform . Coordinates ;
2022-11-22 13:12:04 +11:00
var entry = grid . GetLocal ( coords )
2021-09-28 13:35:29 +02:00
. FirstOrDefault ( entity = > EntityManager . HasComponent < DisposalEntryComponent > ( entity ) ) ;
2021-08-12 13:40:38 +10:00
if ( entry = = default )
{
return false ;
}
var air = component . Air ;
2021-09-28 13:35:29 +02:00
var entryComponent = EntityManager . GetComponent < DisposalEntryComponent > ( entry ) ;
2022-07-04 16:51:34 +02:00
var indices = _transformSystem . GetGridOrMapTilePosition ( component . Owner , xform ) ;
2021-08-12 13:40:38 +10:00
2022-07-04 16:51:34 +02:00
if ( _atmosSystem . GetTileMixture ( xform . GridUid , xform . MapUid , indices , true ) is { Temperature : > 0 } environment )
2021-08-12 13:40:38 +10:00
{
2022-02-19 17:42:01 -07:00
var transferMoles = 0.1f * ( 0.25f * Atmospherics . OneAtmosphere * 1.01f - air . Pressure ) * air . Volume / ( environment . Temperature * Atmospherics . R ) ;
2021-08-12 13:40:38 +10:00
component . Air = environment . Remove ( transferMoles ) ;
}
2022-08-14 07:57:25 +02:00
entryComponent . TryInsert ( component , beforeFlushArgs . Tags ) ;
2021-08-12 13:40:38 +10:00
component . AutomaticEngageToken ? . Cancel ( ) ;
component . AutomaticEngageToken = null ;
component . Pressure = 0 ;
2022-01-30 17:53:22 +01:00
component . State = component . Pressure > = 1 ? SharedDisposalUnitComponent . PressureState . Ready : SharedDisposalUnitComponent . PressureState . Pressurizing ;
2021-08-12 13:40:38 +10:00
component . Engaged = false ;
HandleStateChange ( component , true ) ;
UpdateVisualState ( component , true ) ;
UpdateInterface ( component , component . Powered ) ;
return true ;
}
public void UpdateInterface ( DisposalUnitComponent component , bool powered )
{
2022-10-06 17:53:36 +03:00
var stateString = Loc . GetString ( $"disposal-unit-state-{component.State}" ) ;
2022-09-11 16:50:59 +10:00
var state = new SharedDisposalUnitComponent . DisposalUnitBoundUserInterfaceState ( Name ( component . Owner ) , stateString , EstimatedFullPressure ( component ) , powered , component . Engaged ) ;
_ui . TrySetUiState ( component . Owner , SharedDisposalUnitComponent . DisposalUnitUiKey . Key , state ) ;
2022-08-14 07:57:25 +02:00
var stateUpdatedEvent = new DisposalUnitUIStateUpdatedEvent ( state ) ;
2022-09-11 16:50:59 +10:00
RaiseLocalEvent ( component . Owner , stateUpdatedEvent ) ;
2021-08-12 13:40:38 +10:00
}
private TimeSpan EstimatedFullPressure ( DisposalUnitComponent component )
{
if ( component . State = = SharedDisposalUnitComponent . PressureState . Ready ) return TimeSpan . Zero ;
var currentTime = GameTiming . CurTime ;
var pressure = component . Pressure ;
return TimeSpan . FromSeconds ( currentTime . TotalSeconds + ( 1.0f - pressure ) / PressurePerSecond ) ;
}
public void UpdateVisualState ( DisposalUnitComponent component )
{
UpdateVisualState ( component , false ) ;
}
public void UpdateVisualState ( DisposalUnitComponent component , bool flush )
{
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . TryGetComponent ( component . Owner , out AppearanceComponent ? appearance ) )
2021-08-12 13:40:38 +10:00
{
return ;
}
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . GetComponent < TransformComponent > ( component . Owner ) . Anchored )
2021-08-12 13:40:38 +10:00
{
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . VisualState , SharedDisposalUnitComponent . VisualState . UnAnchored , appearance ) ;
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Handle , SharedDisposalUnitComponent . HandleState . Normal , appearance ) ;
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Light , SharedDisposalUnitComponent . LightState . Off , appearance ) ;
2021-08-12 13:40:38 +10:00
return ;
}
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . VisualState , component . Pressure < 1 ? SharedDisposalUnitComponent . VisualState . Charging : SharedDisposalUnitComponent . VisualState . Anchored , appearance ) ;
2021-08-12 13:40:38 +10:00
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Handle , component . Engaged
2021-08-12 13:40:38 +10:00
? SharedDisposalUnitComponent . HandleState . Engaged
2022-09-11 16:50:59 +10:00
: SharedDisposalUnitComponent . HandleState . Normal , appearance ) ;
2021-08-12 13:40:38 +10:00
if ( ! component . Powered )
{
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Light , SharedDisposalUnitComponent . LightState . Off , appearance ) ;
2021-08-12 13:40:38 +10:00
return ;
}
if ( flush )
{
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . VisualState , SharedDisposalUnitComponent . VisualState . Flushing , appearance ) ;
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Light , SharedDisposalUnitComponent . LightState . Off , appearance ) ;
2021-08-12 13:40:38 +10:00
return ;
}
2022-01-30 17:53:22 +01:00
if ( component . Container . ContainedEntities . Count > 0 )
2021-08-12 13:40:38 +10:00
{
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Light , SharedDisposalUnitComponent . LightState . Full , appearance ) ;
2021-08-12 13:40:38 +10:00
return ;
}
2022-09-11 16:50:59 +10:00
_appearance . SetData ( component . Owner , SharedDisposalUnitComponent . Visuals . Light , component . Pressure < 1
2021-08-12 13:40:38 +10:00
? SharedDisposalUnitComponent . LightState . Charging
2022-09-11 16:50:59 +10:00
: SharedDisposalUnitComponent . LightState . Ready , appearance ) ;
2021-08-12 13:40:38 +10:00
}
2021-12-05 18:09:01 +01:00
public void Remove ( DisposalUnitComponent component , EntityUid entity )
2021-08-12 13:40:38 +10:00
{
component . Container . Remove ( entity ) ;
2022-01-30 17:53:22 +01:00
if ( component . Container . ContainedEntities . Count = = 0 )
2021-08-12 13:40:38 +10:00
{
component . AutomaticEngageToken ? . Cancel ( ) ;
component . AutomaticEngageToken = null ;
}
2021-12-03 15:53:09 +01:00
if ( ! component . RecentlyEjected . Contains ( entity ) )
component . RecentlyEjected . Add ( entity ) ;
2021-08-12 13:40:38 +10:00
2022-02-02 17:34:25 +11:00
Dirty ( component ) ;
2021-08-12 13:40:38 +10:00
HandleStateChange ( component , true ) ;
UpdateVisualState ( component ) ;
}
public bool CanFlush ( DisposalUnitComponent component )
{
2021-12-08 13:00:43 +01:00
return component . State = = SharedDisposalUnitComponent . PressureState . Ready & & component . Powered & & EntityManager . GetComponent < TransformComponent > ( component . Owner ) . Anchored ;
2021-08-12 13:40:38 +10:00
}
public void Engage ( DisposalUnitComponent component )
{
component . Engaged = true ;
UpdateVisualState ( component ) ;
UpdateInterface ( component , component . Powered ) ;
if ( CanFlush ( component ) )
{
component . Owner . SpawnTimer ( component . FlushDelay , ( ) = > TryFlush ( component ) ) ;
}
}
public void Disengage ( DisposalUnitComponent component )
{
component . Engaged = false ;
UpdateVisualState ( component ) ;
UpdateInterface ( component , component . Powered ) ;
}
/// <summary>
/// Remove all entities currently in the disposal unit.
/// </summary>
public void TryEjectContents ( DisposalUnitComponent component )
{
foreach ( var entity in component . Container . ContainedEntities . ToArray ( ) )
{
Remove ( component , entity ) ;
}
}
2021-12-05 18:09:01 +01:00
public override bool CanInsert ( SharedDisposalUnitComponent component , EntityUid entity )
2021-08-12 13:40:38 +10:00
{
if ( ! base . CanInsert ( component , entity ) | | component is not DisposalUnitComponent serverComp )
return false ;
return serverComp . Container . CanInsert ( entity ) ;
}
/// <summary>
/// If something is inserted (or the likes) then we'll queue up a flush in the future.
/// </summary>
public void TryQueueEngage ( DisposalUnitComponent component )
{
2022-08-14 07:57:25 +02:00
if ( component . Deleted | | ! component . AutomaticEngage | | ! component . Powered & & component . Container . ContainedEntities . Count = = 0 )
2021-08-12 13:40:38 +10:00
{
return ;
}
component . AutomaticEngageToken = new CancellationTokenSource ( ) ;
2022-01-30 17:53:22 +01:00
component . Owner . SpawnTimer ( component . AutomaticEngageTime , ( ) = >
2021-08-12 13:40:38 +10:00
{
if ( ! TryFlush ( component ) )
{
TryQueueEngage ( component ) ;
}
} , component . AutomaticEngageToken . Token ) ;
}
2021-12-05 18:09:01 +01:00
public void AfterInsert ( DisposalUnitComponent component , EntityUid entity )
2021-08-12 13:40:38 +10:00
{
TryQueueEngage ( component ) ;
2021-12-08 13:00:43 +01:00
if ( EntityManager . TryGetComponent ( entity , out ActorComponent ? actor ) )
2021-08-12 13:40:38 +10:00
{
2022-09-11 16:50:59 +10:00
_ui . TryClose ( component . Owner , SharedDisposalUnitComponent . DisposalUnitUiKey . Key , actor . PlayerSession ) ;
2021-08-12 13:40:38 +10:00
}
UpdateVisualState ( component ) ;
2021-07-16 05:22:29 +02:00
}
2022-09-11 16:50:59 +10:00
}
2022-08-14 07:57:25 +02:00
2022-09-11 16:50:59 +10:00
/// <summary>
/// Sent before the disposal unit flushes it's contents.
/// Allows adding tags for sorting and preventing the disposal unit from flushing.
/// </summary>
public sealed class DisposalUnitUIStateUpdatedEvent : EntityEventArgs
{
public SharedDisposalUnitComponent . DisposalUnitBoundUserInterfaceState State ;
2022-08-14 07:57:25 +02:00
2022-09-11 16:50:59 +10:00
public DisposalUnitUIStateUpdatedEvent ( SharedDisposalUnitComponent . DisposalUnitBoundUserInterfaceState state )
2022-08-14 07:57:25 +02:00
{
2022-09-11 16:50:59 +10:00
State = state ;
2022-08-14 07:57:25 +02:00
}
2021-05-13 02:05:46 +02:00
}
2022-09-11 16:50:59 +10:00
/// <summary>
/// Sent before the disposal unit flushes it's contents.
/// Allows adding tags for sorting and preventing the disposal unit from flushing.
/// </summary>
public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs
{
public readonly List < string > Tags = new ( ) ;
}
2021-05-13 02:05:46 +02:00
}