using System.Diagnostics.CodeAnalysis; using Content.Server.Atmos.EntitySystems; using Content.Server.Construction; using Content.Server.Construction.Components; using Content.Server.Storage.Components; using Content.Server.Tools.Systems; using Content.Shared.Destructible; using Content.Shared.Foldable; using Content.Shared.Interaction; using Content.Shared.Lock; using Content.Shared.Movement.Events; using Content.Shared.Storage.Components; using Content.Shared.Storage.EntitySystems; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Map; namespace Content.Server.Storage.EntitySystems; public sealed class EntityStorageSystem : SharedEntityStorageSystem { [Dependency] private readonly ConstructionSystem _construction = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly IMapManager _map = default!; public override void Initialize() { base.Initialize(); /* CompRef things */ SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentStartup); SubscribeLocalEvent(OnInteract, after: new[] { typeof(LockSystem) }); SubscribeLocalEvent(OnLockToggleAttempt); SubscribeLocalEvent(OnDestruction); SubscribeLocalEvent>(AddToggleOpenVerb); SubscribeLocalEvent(OnRelayMovement); SubscribeLocalEvent(OnFoldAttempt); SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnHandleState); /* CompRef things */ SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnWeldableAttempt); SubscribeLocalEvent(OnWelded); SubscribeLocalEvent(OnInsideInhale); SubscribeLocalEvent(OnInsideExhale); SubscribeLocalEvent(OnInsideExposed); SubscribeLocalEvent(OnRemoved); } private void OnMapInit(EntityUid uid, EntityStorageComponent component, MapInitEvent args) { if (!component.Open && component.Air.TotalMoles == 0) { // If we're closed on spawn and have no air already saved, we need to pull some air into our environment from where we spawned, // so that we have -something-. For example, if you bought an animal crate or something. TakeGas(uid, component); } } protected override void OnComponentInit(EntityUid uid, SharedEntityStorageComponent component, ComponentInit args) { base.OnComponentInit(uid, component, args); if (TryComp(uid, out var construction)) _construction.AddContainer(uid, ContainerName, construction); } public override bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref SharedEntityStorageComponent? component) { if (component != null) return true; TryComp(uid, out var storage); component = storage; return component != null; } private void OnWeldableAttempt(EntityUid uid, EntityStorageComponent component, WeldableAttemptEvent args) { if (component.Open) { args.Cancel(); return; } if (component.Contents.Contains(args.User)) { var msg = Loc.GetString("entity-storage-component-already-contains-user-message"); Popup.PopupEntity(msg, args.User, args.User); args.Cancel(); } } private void OnWelded(EntityUid uid, EntityStorageComponent component, WeldableChangedEvent args) { component.IsWeldedShut = args.IsWelded; Dirty(component); } protected override void TakeGas(EntityUid uid, SharedEntityStorageComponent component) { if (!component.Airtight) return; var serverComp = (EntityStorageComponent) component; var tile = GetOffsetTileRef(uid, serverComp); if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment) { _atmos.Merge(serverComp.Air, environment.RemoveVolume(serverComp.Air.Volume)); } } public override void ReleaseGas(EntityUid uid, SharedEntityStorageComponent component) { var serverComp = (EntityStorageComponent) component; if (!serverComp.Airtight) return; var tile = GetOffsetTileRef(uid, serverComp); if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment) { _atmos.Merge(environment, serverComp.Air); serverComp.Air.Clear(); } } private TileRef? GetOffsetTileRef(EntityUid uid, EntityStorageComponent component) { var targetCoordinates = new EntityCoordinates(uid, component.EnteringOffset).ToMap(EntityManager); if (_map.TryFindGridAt(targetCoordinates, out _, out var grid)) { return grid.GetTileRef(targetCoordinates); } return null; } private void OnRemoved(EntityUid uid, InsideEntityStorageComponent component, EntGotRemovedFromContainerMessage args) { if (args.Container.Owner != component.Storage) return; RemComp(uid, component); } #region Gas mix event handlers private void OnInsideInhale(EntityUid uid, InsideEntityStorageComponent component, InhaleLocationEvent args) { if (TryComp(component.Storage, out var storage) && storage.Airtight) { args.Gas = storage.Air; } } private void OnInsideExhale(EntityUid uid, InsideEntityStorageComponent component, ExhaleLocationEvent args) { if (TryComp(component.Storage, out var storage) && storage.Airtight) { args.Gas = storage.Air; } } private void OnInsideExposed(EntityUid uid, InsideEntityStorageComponent component, ref AtmosExposedGetAirEvent args) { if (args.Handled) return; if (TryComp(component.Storage, out var storage)) { if (!storage.Airtight) return; args.Gas = storage.Air; } args.Handled = true; } #endregion }