2022-10-04 02:57:32 +02:00
using Content.Server.Administration.Logs ;
2021-10-05 14:29:03 +11:00
using Content.Server.Chemistry.Components ;
2023-12-29 04:47:43 -08:00
using Content.Server.Chemistry.Containers.EntitySystems ;
2023-08-23 15:12:11 +03:00
using Content.Server.DeviceLinking.Systems ;
2022-10-04 02:57:32 +02:00
using Content.Shared.Chemistry ;
using Content.Shared.Chemistry.Dispenser ;
2023-10-14 09:45:28 -07:00
using Content.Shared.Chemistry.EntitySystems ;
2023-09-05 09:55:10 +12:00
using Content.Shared.Chemistry.Reagent ;
2022-10-04 02:57:32 +02:00
using Content.Shared.Containers.ItemSlots ;
using Content.Shared.Database ;
2023-08-23 15:12:11 +03:00
using Content.Shared.DeviceLinking ;
using Content.Shared.DeviceLinking.Events ;
2023-02-19 01:03:06 +00:00
using Content.Shared.Emag.Components ;
2022-02-17 21:43:24 -05:00
using Content.Shared.Emag.Systems ;
2021-09-06 15:49:44 +02:00
using JetBrains.Annotations ;
2023-11-27 22:12:34 +11:00
using Robust.Server.Audio ;
2022-10-04 02:57:32 +02:00
using Robust.Server.GameObjects ;
using Robust.Shared.Audio ;
2021-11-02 09:04:07 +00:00
using Robust.Shared.Containers ;
2022-10-04 02:57:32 +02:00
using Robust.Shared.Prototypes ;
2023-12-29 04:47:43 -08:00
using System.Linq ;
2023-08-23 15:12:11 +03:00
using Content.Shared.Chemistry.Components.SolutionManager ;
using Content.Shared.FixedPoint ;
2021-09-06 15:49:44 +02:00
namespace Content.Server.Chemistry.EntitySystems
{
2022-10-04 02:57:32 +02:00
/// <summary>
/// Contains all the server-side logic for reagent dispensers.
/// <seealso cref="ReagentDispenserComponent"/>
/// </summary>
2021-09-06 15:49:44 +02:00
[UsedImplicitly]
2022-10-04 02:57:32 +02:00
public sealed class ReagentDispenserSystem : EntitySystem
2021-09-06 15:49:44 +02:00
{
2022-10-04 02:57:32 +02:00
[Dependency] private readonly AudioSystem _audioSystem = default ! ;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default ! ;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default ! ;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default ! ;
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2023-08-23 15:12:11 +03:00
[Dependency] private readonly DeviceLinkSystem _signalSystem = default ! ; // WD
[Dependency] private readonly ChemMasterSystem _chemMasterSystem = default ! ; // WD
2021-09-06 15:49:44 +02:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-10-04 02:57:32 +02:00
2023-10-19 12:34:31 -07:00
SubscribeLocalEvent < ReagentDispenserComponent , ComponentStartup > ( SubscribeUpdateUiState ) ;
2023-12-29 04:47:43 -08:00
SubscribeLocalEvent < ReagentDispenserComponent , SolutionContainerChangedEvent > ( SubscribeUpdateUiState ) ;
2023-10-19 12:34:31 -07:00
SubscribeLocalEvent < ReagentDispenserComponent , EntInsertedIntoContainerMessage > ( SubscribeUpdateUiState ) ;
SubscribeLocalEvent < ReagentDispenserComponent , EntRemovedFromContainerMessage > ( SubscribeUpdateUiState ) ;
SubscribeLocalEvent < ReagentDispenserComponent , BoundUIOpenedEvent > ( SubscribeUpdateUiState ) ;
2023-08-23 15:12:11 +03:00
// WD START
SubscribeLocalEvent < ReagentDispenserComponent , ComponentInit > ( OnInit ) ;
SubscribeLocalEvent < ReagentDispenserComponent , MapInitEvent > ( OnMapInit ) ;
SubscribeLocalEvent < ReagentDispenserComponent , NewLinkEvent > ( OnNewLink ) ;
SubscribeLocalEvent < ReagentDispenserComponent , PortDisconnectedEvent > ( OnPortDisconnected ) ;
SubscribeLocalEvent < ReagentDispenserComponent , AnchorStateChangedEvent > ( OnAnchorChanged ) ;
// WD END
2022-02-17 21:43:24 -05:00
SubscribeLocalEvent < ReagentDispenserComponent , GotEmaggedEvent > ( OnEmagged ) ;
2022-10-04 02:57:32 +02:00
SubscribeLocalEvent < ReagentDispenserComponent , ReagentDispenserSetDispenseAmountMessage > ( OnSetDispenseAmountMessage ) ;
SubscribeLocalEvent < ReagentDispenserComponent , ReagentDispenserDispenseReagentMessage > ( OnDispenseReagentMessage ) ;
SubscribeLocalEvent < ReagentDispenserComponent , ReagentDispenserClearContainerSolutionMessage > ( OnClearContainerSolutionMessage ) ;
}
2023-08-23 15:12:11 +03:00
// WD START
private void OnInit ( EntityUid uid , ReagentDispenserComponent component , ComponentInit args )
{
_signalSystem . EnsureSourcePorts ( uid , ReagentDispenserComponent . ChemMasterPort ) ;
}
private void OnMapInit ( EntityUid uid , ReagentDispenserComponent component , MapInitEvent args )
{
if ( ! TryComp < DeviceLinkSourceComponent > ( uid , out var receiver ) )
return ;
foreach ( var port in receiver . Outputs . Values . SelectMany ( ports = > ports ) )
{
if ( ! TryComp < ChemMasterComponent > ( port , out var master ) )
continue ;
UpdateConnection ( uid , port , component , master ) ;
break ;
}
}
private void OnNewLink ( EntityUid uid , ReagentDispenserComponent component , NewLinkEvent args )
{
if ( TryComp < ChemMasterComponent > ( args . Sink , out var master ) & & args . SourcePort = = ReagentDispenserComponent . ChemMasterPort )
UpdateConnection ( uid , args . Sink , component , master ) ;
}
private void OnPortDisconnected ( EntityUid uid , ReagentDispenserComponent component , PortDisconnectedEvent args )
{
if ( args . Port ! = ReagentDispenserComponent . ChemMasterPort )
return ;
component . ChemMaster = null ;
component . ChemMasterInRange = false ;
}
private void OnAnchorChanged ( EntityUid uid , ReagentDispenserComponent component , ref AnchorStateChangedEvent args )
{
if ( args . Anchored )
RecheckConnections ( uid , component ) ;
}
public void UpdateConnection ( EntityUid dispenser , EntityUid chemMaster ,
ReagentDispenserComponent ? dispenserComp = null , ChemMasterComponent ? chemMasterComp = null )
{
if ( ! Resolve ( dispenser , ref dispenserComp ) | | ! Resolve ( chemMaster , ref chemMasterComp ) )
return ;
if ( dispenserComp . ChemMaster . HasValue & & dispenserComp . ChemMaster . Value ! = chemMaster & &
TryComp ( dispenserComp . ChemMaster , out ChemMasterComponent ? oldMaster ) )
{
oldMaster . ConnectedDispenser = null ;
}
if ( chemMasterComp . ConnectedDispenser . HasValue & & chemMasterComp . ConnectedDispenser . Value ! = dispenser & &
TryComp ( dispenserComp . ChemMaster , out ReagentDispenserComponent ? oldDispenser ) )
{
oldDispenser . ChemMaster = null ;
oldDispenser . ChemMasterInRange = false ;
}
dispenserComp . ChemMaster = chemMaster ;
chemMasterComp . ConnectedDispenser = dispenser ;
RecheckConnections ( dispenser , dispenserComp ) ;
}
private void RecheckConnections ( EntityUid dispenser , ReagentDispenserComponent ? component = null )
{
if ( ! Resolve ( dispenser , ref component ) )
return ;
if ( component . ChemMaster = = null )
{
component . ChemMasterInRange = false ;
return ;
}
Transform ( component . ChemMaster . Value ) . Coordinates
. TryDistance ( EntityManager , Transform ( dispenser ) . Coordinates , out var distance ) ;
component . ChemMasterInRange = distance < = 1.5f ;
}
// WD END
2023-10-19 12:34:31 -07:00
private void SubscribeUpdateUiState < T > ( Entity < ReagentDispenserComponent > ent , ref T ev )
2022-10-04 02:57:32 +02:00
{
2023-10-19 12:34:31 -07:00
UpdateUiState ( ent ) ;
}
private void UpdateUiState ( Entity < ReagentDispenserComponent > reagentDispenser )
{
var outputContainer = _itemSlotsSystem . GetItemOrNull ( reagentDispenser , SharedReagentDispenser . OutputSlotName ) ;
2022-10-04 02:57:32 +02:00
var outputContainerInfo = BuildOutputContainerInfo ( outputContainer ) ;
var inventory = GetInventory ( reagentDispenser ) ;
2023-10-19 12:34:31 -07:00
var state = new ReagentDispenserBoundUserInterfaceState ( outputContainerInfo , inventory , reagentDispenser . Comp . DispenseAmount ) ;
_userInterfaceSystem . TrySetUiState ( reagentDispenser , ReagentDispenserUiKey . Key , state ) ;
2022-10-04 02:57:32 +02:00
}
private ContainerInfo ? BuildOutputContainerInfo ( EntityUid ? container )
{
if ( container is not { Valid : true } )
return null ;
2023-12-29 04:47:43 -08:00
if ( _solutionContainerSystem . TryGetFitsInDispenser ( container . Value , out _ , out var solution ) )
2022-10-04 02:57:32 +02:00
{
2023-09-05 09:55:10 +12:00
return new ContainerInfo ( Name ( container . Value ) , solution . Volume , solution . MaxVolume )
{
Reagents = solution . Contents
} ;
2022-10-04 02:57:32 +02:00
}
return null ;
}
2023-10-19 12:34:31 -07:00
private List < ReagentId > GetInventory ( Entity < ReagentDispenserComponent > ent )
2022-10-04 02:57:32 +02:00
{
2023-10-19 12:34:31 -07:00
var reagentDispenser = ent . Comp ;
2023-09-05 09:55:10 +12:00
var inventory = new List < ReagentId > ( ) ;
2022-10-04 02:57:32 +02:00
if ( reagentDispenser . PackPrototypeId is not null
& & _prototypeManager . TryIndex ( reagentDispenser . PackPrototypeId , out ReagentDispenserInventoryPrototype ? packPrototype ) )
{
2023-09-05 09:55:10 +12:00
inventory . AddRange ( packPrototype . Inventory . Select ( x = > new ReagentId ( x , null ) ) ) ;
2022-10-04 02:57:32 +02:00
}
2023-10-19 12:34:31 -07:00
if ( HasComp < EmaggedComponent > ( ent )
2022-10-04 02:57:32 +02:00
& & reagentDispenser . EmagPackPrototypeId is not null
& & _prototypeManager . TryIndex ( reagentDispenser . EmagPackPrototypeId , out ReagentDispenserInventoryPrototype ? emagPackPrototype ) )
{
2023-09-05 09:55:10 +12:00
inventory . AddRange ( emagPackPrototype . Inventory . Select ( x = > new ReagentId ( x , null ) ) ) ;
2022-10-04 02:57:32 +02:00
}
return inventory ;
2022-02-17 21:43:24 -05:00
}
2023-10-19 12:34:31 -07:00
private void OnEmagged ( Entity < ReagentDispenserComponent > reagentDispenser , ref GotEmaggedEvent args )
2022-02-17 21:43:24 -05:00
{
2023-02-19 01:03:06 +00:00
// adding component manually to have correct state
2023-10-19 12:34:31 -07:00
EntityManager . AddComponent < EmaggedComponent > ( reagentDispenser ) ;
2023-02-19 01:03:06 +00:00
UpdateUiState ( reagentDispenser ) ;
args . Handled = true ;
2021-09-06 15:49:44 +02:00
}
2022-10-04 02:57:32 +02:00
2023-10-19 12:34:31 -07:00
private void OnSetDispenseAmountMessage ( Entity < ReagentDispenserComponent > reagentDispenser , ref ReagentDispenserSetDispenseAmountMessage message )
2022-10-04 02:57:32 +02:00
{
2023-10-19 12:34:31 -07:00
reagentDispenser . Comp . DispenseAmount = message . ReagentDispenserDispenseAmount ;
2022-10-04 02:57:32 +02:00
UpdateUiState ( reagentDispenser ) ;
ClickSound ( reagentDispenser ) ;
}
2023-10-19 12:34:31 -07:00
private void OnDispenseReagentMessage ( Entity < ReagentDispenserComponent > reagentDispenser , ref ReagentDispenserDispenseReagentMessage message )
2022-10-04 02:57:32 +02:00
{
// Ensure that the reagent is something this reagent dispenser can dispense.
if ( ! GetInventory ( reagentDispenser ) . Contains ( message . ReagentId ) )
return ;
2023-10-19 12:34:31 -07:00
var outputContainer = _itemSlotsSystem . GetItemOrNull ( reagentDispenser , SharedReagentDispenser . OutputSlotName ) ;
2023-12-29 04:47:43 -08:00
if ( outputContainer is not { Valid : true } | | ! _solutionContainerSystem . TryGetFitsInDispenser ( outputContainer . Value , out var solution , out _ ) )
2023-08-23 15:12:11 +03:00
{ // WD EDIT START
var chemMasterUid = reagentDispenser . Comp . ChemMaster ;
if ( ! reagentDispenser . Comp . ChemMasterInRange | |
! TryComp ( chemMasterUid , out ChemMasterComponent ? chemMaster ) | |
! TryComp ( chemMasterUid , out SolutionContainerManagerComponent ? solutionContainer ) | |
! _solutionContainerSystem . TryGetSolution ( ( chemMasterUid . Value , solutionContainer ) ,
SharedChemMaster . BufferSolutionName , out var bufferSolution ) )
return ;
bufferSolution . Value . Comp . Solution . AddReagent ( message . ReagentId , FixedPoint2 . New ( ( int ) reagentDispenser . Comp . DispenseAmount ) ) ;
_chemMasterSystem . UpdateUiState ( ( chemMasterUid . Value , chemMaster ) ) ;
2023-09-04 12:03:28 +03:00
ClickSound ( reagentDispenser ) ;
2023-08-23 15:12:11 +03:00
2022-10-04 02:57:32 +02:00
return ;
2023-08-23 15:12:11 +03:00
} // WD EDIT END
2022-10-04 02:57:32 +02:00
2023-12-29 04:47:43 -08:00
if ( _solutionContainerSystem . TryAddReagent ( solution . Value , message . ReagentId , ( int ) reagentDispenser . Comp . DispenseAmount , out var dispensedAmount )
2022-10-04 02:57:32 +02:00
& & message . Session . AttachedEntity is not null )
{
_adminLogger . Add ( LogType . ChemicalReaction , LogImpact . Medium ,
$"{ToPrettyString(message.Session.AttachedEntity.Value):player} dispensed {dispensedAmount}u of {message.ReagentId} into {ToPrettyString(outputContainer.Value):entity}" ) ;
}
UpdateUiState ( reagentDispenser ) ;
ClickSound ( reagentDispenser ) ;
}
2023-10-19 12:34:31 -07:00
private void OnClearContainerSolutionMessage ( Entity < ReagentDispenserComponent > reagentDispenser , ref ReagentDispenserClearContainerSolutionMessage message )
2022-10-04 02:57:32 +02:00
{
2023-10-19 12:34:31 -07:00
var outputContainer = _itemSlotsSystem . GetItemOrNull ( reagentDispenser , SharedReagentDispenser . OutputSlotName ) ;
2023-12-29 04:47:43 -08:00
if ( outputContainer is not { Valid : true } | | ! _solutionContainerSystem . TryGetFitsInDispenser ( outputContainer . Value , out var solution , out _ ) )
2022-10-04 02:57:32 +02:00
return ;
2023-12-29 04:47:43 -08:00
_solutionContainerSystem . RemoveAllSolution ( solution . Value ) ;
2022-10-04 02:57:32 +02:00
UpdateUiState ( reagentDispenser ) ;
ClickSound ( reagentDispenser ) ;
}
2023-10-19 12:34:31 -07:00
private void ClickSound ( Entity < ReagentDispenserComponent > reagentDispenser )
2022-10-04 02:57:32 +02:00
{
2023-10-19 12:34:31 -07:00
_audioSystem . PlayPvs ( reagentDispenser . Comp . ClickSound , reagentDispenser , AudioParams . Default . WithVolume ( - 2f ) ) ;
2022-10-04 02:57:32 +02:00
}
2021-09-06 15:49:44 +02:00
}
}