diff --git a/Content.Client/Disposal/Components/DisposalUnitComponent.cs b/Content.Client/Disposal/Components/DisposalUnitComponent.cs index afbd22616b..aa51b58ff1 100644 --- a/Content.Client/Disposal/Components/DisposalUnitComponent.cs +++ b/Content.Client/Disposal/Components/DisposalUnitComponent.cs @@ -1,6 +1,6 @@ -using Content.Shared.Disposal.Components; -using Content.Shared.DragDrop; -using Robust.Shared.GameObjects; +using Content.Shared.Disposal.Components; +using Robust.Client.Animations; +using Robust.Shared.Audio; namespace Content.Client.Disposal.Components { @@ -8,6 +8,11 @@ namespace Content.Client.Disposal.Components [ComponentReference(typeof(SharedDisposalUnitComponent))] public sealed class DisposalUnitComponent : SharedDisposalUnitComponent { + [DataField("flushSound")] + public readonly SoundSpecifier? FlushSound; + + public Animation FlushAnimation = default!; + public DisposalUnitBoundUserInterfaceState? UiState; public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs index cc866db8d9..2a531e9a41 100644 --- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs +++ b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs @@ -1,59 +1,186 @@ -using System.Collections.Generic; using Content.Client.Disposal.Components; using Content.Client.Disposal.UI; using Content.Shared.Disposal; using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; +using Robust.Client.Animations; +using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; -namespace Content.Client.Disposal.Systems +namespace Content.Client.Disposal.Systems; + +public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { - public sealed class DisposalUnitSystem : SharedDisposalUnitSystem + [Dependency] private readonly AppearanceSystem AppearanceSystem = default!; + [Dependency] private readonly AnimationPlayerSystem AnimationSystem = default!; + [Dependency] private readonly SharedAudioSystem SoundSystem = default!; + private const string AnimationKey = "disposal_unit_animation"; + + private List PressuringDisposals = new(); + + public override void Initialize() { - public List PressuringDisposals = new(); + base.Initialize(); - public void UpdateActive(DisposalUnitComponent component, bool active) + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnAppearanceChange); + } + + public void UpdateActive(EntityUid disposalEntity, bool active) + { + if (active) { - if (active) - { - if (!PressuringDisposals.Contains(component)) - PressuringDisposals.Add(component); - } - else - { - PressuringDisposals.Remove(component); - } + if (!PressuringDisposals.Contains(disposalEntity)) + PressuringDisposals.Add(disposalEntity); } - - public override void FrameUpdate(float frameTime) + else { - base.FrameUpdate(frameTime); - for (var i = PressuringDisposals.Count - 1; i >= 0; i--) - { - var comp = PressuringDisposals[i]; - if (!UpdateInterface(comp)) continue; - PressuringDisposals.RemoveAt(i); - } - } - - private bool UpdateInterface(DisposalUnitComponent component) - { - if (component.Deleted) return true; - - if (!EntityManager.TryGetComponent(component.Owner, out ClientUserInterfaceComponent? userInterface)) return true; - - var state = component.UiState; - if (state == null) return true; - - foreach (var inter in userInterface.Interfaces) - { - if (inter is DisposalUnitBoundUserInterface boundInterface) - { - return boundInterface.UpdateWindowState(state) != false; - } - } - - return true; + PressuringDisposals.Remove(disposalEntity); } } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + for (var i = PressuringDisposals.Count - 1; i >= 0; i--) + { + var disposal = PressuringDisposals[i]; + if (!UpdateInterface(disposal)) + continue; + + PressuringDisposals.RemoveAt(i); + } + } + + private bool UpdateInterface(EntityUid disposalUnit) + { + if (!TryComp(disposalUnit, out DisposalUnitComponent? component) || component.Deleted) + return true; + if (component.Deleted) + return true; + if (!TryComp(disposalUnit, out ClientUserInterfaceComponent? userInterface)) + return true; + + var state = component.UiState; + if (state == null) + return true; + + foreach (var inter in userInterface.Interfaces) + { + if (inter is DisposalUnitBoundUserInterface boundInterface) + { + return boundInterface.UpdateWindowState(state) != false; + } + } + + return true; + } + + private void OnComponentInit(EntityUid uid, DisposalUnitComponent disposalUnit, ComponentInit args) + { + if (!TryComp(uid, out var sprite)) + return; + + if(!sprite.LayerMapTryGet(DisposalUnitVisualLayers.Base, out var baseLayerIdx)) + return; // Couldn't find the "normal" layer to return to after flush animation + + if(!sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseFlush, out var flushLayerIdx)) + return; // Couldn't find the flush animation layer + + var originalBaseState = sprite.LayerGetState(baseLayerIdx); + var flushState = sprite.LayerGetState(flushLayerIdx); + + // Setup the flush animation to play + disposalUnit.FlushAnimation = new Animation { + Length = TimeSpan.FromSeconds(disposalUnit.FlushTime), + AnimationTracks = { + new AnimationTrackSpriteFlick { + LayerKey = DisposalUnitVisualLayers.BaseFlush, + KeyFrames = { + // Play the flush animation + new AnimationTrackSpriteFlick.KeyFrame(flushState, 0), + // Return to base state (though, depending on how the unit is + // configured we might get an appearence change event telling + // us to go to charging state) + new AnimationTrackSpriteFlick.KeyFrame(originalBaseState, disposalUnit.FlushTime) + } + }, + } + }; + + if (disposalUnit.FlushSound != null) + { + disposalUnit.FlushAnimation.AnimationTracks.Add( + new AnimationTrackPlaySound { + KeyFrames = { + new AnimationTrackPlaySound.KeyFrame(SoundSystem.GetSound(disposalUnit.FlushSound), 0) + } + }); + } + + EnsureComp(uid); + + UpdateState(uid, disposalUnit, sprite); + } + + private void OnAppearanceChange(EntityUid uid, DisposalUnitComponent unit, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + { + return; + } + + UpdateState(uid, unit, args.Sprite); + } + + // Update visuals and tick animation + private void UpdateState(EntityUid uid, DisposalUnitComponent unit, SpriteComponent sprite) + { + if (!AppearanceSystem.TryGetData(uid, Visuals.VisualState, out var state)) + { + return; + } + + sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored); + sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored); + sprite.LayerSetVisible(DisposalUnitVisualLayers.BaseCharging, state == VisualState.Charging); + sprite.LayerSetVisible(DisposalUnitVisualLayers.BaseFlush, state == VisualState.Flushing); + + if (state == VisualState.Flushing) + { + if (!AnimationSystem.HasRunningAnimation(uid, AnimationKey)) + { + AnimationSystem.Play(uid, unit.FlushAnimation, AnimationKey); + } + } + + if (!AppearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState)) + { + handleState = HandleState.Normal; + } + + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal); + + if (!AppearanceSystem.TryGetData(uid, Visuals.Light, out var lightState)) + { + lightState = LightStates.Off; + } + + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging, + (lightState & LightStates.Charging) != 0); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady, + (lightState & LightStates.Ready) != 0); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull, + (lightState & LightStates.Full) != 0); + } +} + +public enum DisposalUnitVisualLayers : byte +{ + Unanchored, + Base, + BaseCharging, + BaseFlush, + OverlayCharging, + OverlayReady, + OverlayFull, + OverlayEngaged } diff --git a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs index eaa2b9912e..4a56687494 100644 --- a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs +++ b/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs @@ -1,12 +1,9 @@ -using Content.Client.Disposal.Components; +using Content.Client.Disposal.Components; using Content.Client.Disposal.Systems; using Content.Shared.Disposal; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; namespace Content.Client.Disposal.UI @@ -17,11 +14,14 @@ namespace Content.Client.Disposal.UI [UsedImplicitly] public sealed class DisposalUnitBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IEntityManager _entityManager = default!; + public MailingUnitWindow? MailingUnitWindow; public DisposalUnitWindow? DisposalUnitWindow; public DisposalUnitBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey) { + IoCManager.InjectDependencies(this); } private void ButtonPressed(UiButton button) @@ -76,7 +76,9 @@ namespace Content.Client.Disposal.UI return; } - if (!IoCManager.Resolve().TryGetComponent(Owner.Owner, out DisposalUnitComponent? component)) return; + var entityId = Owner.Owner; + if (!_entityManager.TryGetComponent(entityId, out DisposalUnitComponent? component)) + return; switch (state) { @@ -91,7 +93,7 @@ namespace Content.Client.Disposal.UI break; } - EntitySystem.Get().UpdateActive(component, true); + _entityManager.System().UpdateActive(entityId, true); } protected override void Dispose(bool disposing) diff --git a/Content.Client/Disposal/Visualizers/DisposalUnitVisualizer.cs b/Content.Client/Disposal/Visualizers/DisposalUnitVisualizer.cs deleted file mode 100644 index 113765b989..0000000000 --- a/Content.Client/Disposal/Visualizers/DisposalUnitVisualizer.cs +++ /dev/null @@ -1,169 +0,0 @@ -using JetBrains.Annotations; -using Robust.Client.Animations; -using Robust.Client.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Serialization; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.Visualizers -{ - [UsedImplicitly] - public sealed class DisposalUnitVisualizer : AppearanceVisualizer, ISerializationHooks - { - private const string AnimationKey = "disposal_unit_animation"; - - [DataField("state_anchored", required: true)] - private string? _stateAnchored; - - [DataField("state_unanchored", required: true)] - private string? _stateUnAnchored; - - [DataField("state_charging", required: true)] - private string? _stateCharging; - - [DataField("overlay_charging", required: true)] - private string? _overlayCharging; - - [DataField("overlay_ready", required: true)] - private string? _overlayReady; - - [DataField("overlay_full", required: true)] - private string? _overlayFull; - - [DataField("overlay_engaged", required: true)] - private string? _overlayEngaged; - - [DataField("state_flush", required: true)] - private string? _stateFlush; - - [DataField("flush_sound", required: true)] - private SoundSpecifier _flushSound = default!; - - [DataField("flush_time", required: true)] - private float _flushTime; - - private Animation _flushAnimation = default!; - - void ISerializationHooks.AfterDeserialization() - { - _flushAnimation = new Animation {Length = TimeSpan.FromSeconds(_flushTime)}; - - var flick = new AnimationTrackSpriteFlick(); - _flushAnimation.AnimationTracks.Add(flick); - flick.LayerKey = DisposalUnitVisualLayers.Base; - flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(_stateFlush, 0)); - - var sound = new AnimationTrackPlaySound(); - _flushAnimation.AnimationTracks.Add(sound); - - sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_flushSound.GetSound(), 0)); - } - - private void ChangeState(AppearanceComponent appearance) - { - if (!appearance.TryGetData(Visuals.VisualState, out VisualState state)) - { - return; - } - - var entities = IoCManager.Resolve(); - if (!entities.TryGetComponent(appearance.Owner, out SpriteComponent? sprite)) - { - return; - } - - switch (state) - { - case VisualState.UnAnchored: - sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateUnAnchored); - break; - case VisualState.Anchored: - sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateAnchored); - break; - case VisualState.Charging: - sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateCharging); - break; - case VisualState.Flushing: - sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateAnchored); - - var animPlayer = entities.GetComponent(appearance.Owner); - - if (!animPlayer.HasRunningAnimation(AnimationKey)) - { - animPlayer.Play(_flushAnimation, AnimationKey); - } - - break; - default: - throw new ArgumentOutOfRangeException(); - } - - if (!appearance.TryGetData(Visuals.Handle, out HandleState handleState)) - { - handleState = HandleState.Normal; - } - - sprite.LayerSetVisible(DisposalUnitVisualLayers.Handle, handleState != HandleState.Normal); - - switch (handleState) - { - case HandleState.Normal: - break; - case HandleState.Engaged: - sprite.LayerSetState(DisposalUnitVisualLayers.Handle, _overlayEngaged); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - if (!appearance.TryGetData(Visuals.Light, out LightState lightState)) - { - lightState = LightState.Off; - } - - sprite.LayerSetVisible(DisposalUnitVisualLayers.Light, lightState != LightState.Off); - - switch (lightState) - { - case LightState.Off: - break; - case LightState.Charging: - sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayCharging); - break; - case LightState.Full: - sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayFull); - break; - case LightState.Ready: - sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayReady); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - [Obsolete("Subscribe to your component being initialised instead.")] - public override void InitializeEntity(EntityUid entity) - { - base.InitializeEntity(entity); - var entities = IoCManager.Resolve(); - entities.EnsureComponent(entity); - var appearance = entities.EnsureComponent(entity); - - ChangeState(appearance); - } - - [Obsolete("Subscribe to AppearanceChangeEvent instead.")] - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - ChangeState(component); - } - } - - public enum DisposalUnitVisualLayers : byte - { - Base, - Handle, - Light - } -} diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index e6f7d0effc..6e29477715 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -62,12 +62,12 @@ namespace Content.IntegrationTests.Tests.Disposal UnitContains(unit, result, entities); } - private void Flush(DisposalUnitComponent unit, bool result, params EntityUid[] entities) + private void Flush(EntityUid unitEntity, DisposalUnitComponent unit, bool result, params EntityUid[] entities) { Assert.That(unit.Container.ContainedEntities, Is.SupersetOf(entities)); Assert.That(entities.Length, Is.EqualTo(unit.Container.ContainedEntities.Count)); - Assert.That(result, Is.EqualTo(EntitySystem.Get().TryFlush(unit))); + Assert.That(result, Is.EqualTo(EntitySystem.Get().TryFlush(unitEntity, unit))); Assert.That(result || entities.Length == 0, Is.EqualTo(unit.Container.ContainedEntities.Count == 0)); } @@ -119,6 +119,7 @@ namespace Content.IntegrationTests.Tests.Disposal - type: DisposalUnit entryDelay: 0 draggedEntryDelay: 0 + flushTime: 0 - type: Anchorable - type: ApcPowerReceiver - type: Physics @@ -151,7 +152,7 @@ namespace Content.IntegrationTests.Tests.Disposal EntityUid wrench = default!; EntityUid disposalUnit = default!; EntityUid disposalTrunk = default!; - DisposalUnitComponent unit = default!; + DisposalUnitComponent unitComponent = default!; var entityManager = server.ResolveDependency(); @@ -166,28 +167,28 @@ namespace Content.IntegrationTests.Tests.Disposal IoCManager.Resolve().GetComponent(disposalUnit).MapPosition); // Test for components existing - ref DisposalUnitComponent? comp = ref unit!; + ref DisposalUnitComponent? comp = ref unitComponent!; Assert.True(entityManager.TryGetComponent(disposalUnit, out comp)); Assert.True(entityManager.HasComponent(disposalTrunk)); // Can't insert, unanchored and unpowered - entityManager.GetComponent(unit!.Owner).Anchored = false; - UnitInsertContains(unit, false, human, wrench, disposalUnit, disposalTrunk); + entityManager.GetComponent(unitComponent!.Owner).Anchored = false; + UnitInsertContains(unitComponent, false, human, wrench, disposalUnit, disposalTrunk); }); await server.WaitAssertion(() => { // Anchor the disposal unit - entityManager.GetComponent(unit.Owner).Anchored = true; + entityManager.GetComponent(unitComponent.Owner).Anchored = true; // No power - Assert.False(unit.Powered); + Assert.False(unitComponent.Powered); // Can't insert the trunk or the unit into itself - UnitInsertContains(unit, false, disposalUnit, disposalTrunk); + UnitInsertContains(unitComponent, false, disposalUnit, disposalTrunk); // Can insert mobs and items - UnitInsertContains(unit, true, human, wrench); + UnitInsertContains(unitComponent, true, human, wrench); }); await server.WaitAssertion(() => @@ -196,7 +197,7 @@ namespace Content.IntegrationTests.Tests.Disposal entityManager.GetComponent(disposalTrunk).WorldPosition += (1, 0); // Fail to flush with a mob and an item - Flush(unit, false, human, wrench); + Flush(disposalUnit, unitComponent, false, human, wrench); }); await server.WaitAssertion(() => @@ -205,7 +206,7 @@ namespace Content.IntegrationTests.Tests.Disposal entityManager.GetComponent(disposalTrunk).WorldPosition -= (1, 0); // Fail to flush with a mob and an item, no power - Flush(unit, false, human, wrench); + Flush(disposalUnit, unitComponent, false, human, wrench); }); await server.WaitAssertion(() => @@ -213,16 +214,16 @@ namespace Content.IntegrationTests.Tests.Disposal // Remove power need Assert.True(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent power)); power!.NeedsPower = false; - unit.Powered = true; //Power state changed event doesn't get fired smh + unitComponent.Powered = true; //Power state changed event doesn't get fired smh // Flush with a mob and an item - Flush(unit, true, human, wrench); + Flush(disposalUnit, unitComponent, true, human, wrench); }); await server.WaitAssertion(() => { // Re-pressurizing - Flush(unit, false); + Flush(disposalUnit, unitComponent, false); }); await pairTracker.CleanReturnAsync(); } diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs index a0b4f5e826..8c1a4d376a 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs @@ -39,13 +39,17 @@ namespace Content.Server.Disposal.Unit.EntitySystems // *This ejection also makes the target not collide with the unit.* // *This is on purpose.* + EntityUid? disposalId = null; DisposalUnitComponent? duc = null; if (_mapManager.TryGetGrid(holderTransform.GridUid, out var grid)) { foreach (var contentUid in grid.GetLocal(holderTransform.Coordinates)) { if (EntityManager.TryGetComponent(contentUid, out duc)) + { + disposalId = contentUid; break; + } } } @@ -71,9 +75,9 @@ namespace Content.Server.Disposal.Unit.EntitySystems } } - if (duc != null) + if (disposalId != null && duc != null) { - _disposalUnitSystem.TryEjectContents(duc); + _disposalUnitSystem.TryEjectContents(disposalId.Value, duc); } if (_atmosphereSystem.GetContainingMixture(uid, false, true) is {} environment) diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 102128b178..8439295484 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -41,7 +41,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; - [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -95,7 +94,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems { // Verbs to flush the unit AlternativeVerb flushVerb = new(); - flushVerb.Act = () => Engage(component); + flushVerb.Act = () => Engage(uid, component); flushVerb.Text = Loc.GetString("disposal-flush-verb-get-data-text"); flushVerb.IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png"; flushVerb.Priority = 1; @@ -104,7 +103,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems // Verb to eject the contents AlternativeVerb ejectVerb = new() { - Act = () => TryEjectContents(component), + Act = () => TryEjectContents(uid, component), Category = VerbCategory.Eject, Text = Loc.GetString("disposal-eject-verb-get-data-text") }; @@ -126,11 +125,11 @@ namespace Content.Server.Disposal.Unit.EntitySystems // Add verb to climb inside of the unit, Verb verb = new() { - Act = () => TryInsert(component.Owner, args.User, args.User), + Act = () => TryInsert(uid, args.User, args.User), DoContactInteraction = true, Text = Loc.GetString("disposal-self-insert-verb-get-data-text") }; - // TODO VERN ICON + // TODO VERB ICON // TODO VERB CATEGORY // create a verb category for "enter"? // See also, medical scanner. Also maybe add verbs for entering lockers/body bags? @@ -156,7 +155,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems { _handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}"); - AfterInsert(component, args.Using.Value); + AfterInsert(uid, component, args.Using.Value); } }; @@ -167,7 +166,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems { var toInsert = ev.ToInsert; - if (!EntityManager.TryGetComponent(ev.Unit, out DisposalUnitComponent? unit)) + if (!TryComp(ev.Unit, out DisposalUnitComponent? unit)) { return; } @@ -176,20 +175,20 @@ namespace Content.Server.Disposal.Unit.EntitySystems 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); + $"{ToPrettyString(ev.User.Value):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(ev.Unit)}"); + AfterInsert(ev.Unit, unit, toInsert); } - public void DoInsertDisposalUnit(EntityUid unit, EntityUid toInsert, EntityUid user, DisposalUnitComponent? disposal = null) + public void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, DisposalUnitComponent? disposal = null) { - if (!Resolve(unit, ref disposal)) + if (!Resolve(uid, ref disposal)) return; if (!disposal.Container.Insert(toInsert)) return; - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(unit)}"); - AfterInsert(disposal, toInsert); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}"); + AfterInsert(uid, disposal, toInsert); } public override void Update(float frameTime) @@ -197,10 +196,11 @@ namespace Content.Server.Disposal.Unit.EntitySystems base.Update(frameTime); foreach (var (_, comp) in EntityQuery()) { - if (!Update(comp, frameTime)) + var uid = comp.Owner; + if (!Update(uid, comp, frameTime)) continue; - RemComp(comp.Owner); + RemComp(uid); } } @@ -215,11 +215,11 @@ namespace Content.Server.Disposal.Unit.EntitySystems switch (args.Button) { case SharedDisposalUnitComponent.UiButton.Eject: - TryEjectContents(component); + TryEjectContents(uid, component); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}"); break; case SharedDisposalUnitComponent.UiButton.Engage: - ToggleEngage(component); + ToggleEngage(uid, component); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}"); break; case SharedDisposalUnitComponent.UiButton.Power: @@ -230,17 +230,17 @@ namespace Content.Server.Disposal.Unit.EntitySystems } } - public void ToggleEngage(DisposalUnitComponent component) + public void ToggleEngage(EntityUid uid, DisposalUnitComponent component) { component.Engaged ^= true; if (component.Engaged) { - Engage(component); + Engage(uid, component); } else { - Disengage(component); + Disengage(uid, component); } } #endregion @@ -248,13 +248,13 @@ namespace Content.Server.Disposal.Unit.EntitySystems #region Eventbus Handlers private void HandleActivate(EntityUid uid, DisposalUnitComponent component, ActivateInWorldEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) { return; } args.Handled = true; - _ui.TryOpen(component.Owner, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession); + _ui.TryOpen(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession); } private void HandleAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args) @@ -262,7 +262,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (args.Handled || !args.CanReach) return; - if (!EntityManager.HasComponent(args.User)) + if (!HasComp(args.User)) { return; } @@ -273,7 +273,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems } _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}"); - AfterInsert(component, args.Used); + AfterInsert(uid, component, args.Used); args.Handled = true; } @@ -292,16 +292,16 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (args.User != null) _adminLogger.Add(LogType.Landed, LogImpact.Low, $"{ToPrettyString(args.Thrown)} thrown by {ToPrettyString(args.User.Value):player} landed in {ToPrettyString(uid)}"); - AfterInsert(component, args.Thrown); + AfterInsert(uid, component, args.Thrown); } private void HandleDisposalInit(EntityUid uid, DisposalUnitComponent component, ComponentInit args) { component.Container = _containerSystem.EnsureContainer(uid, SharedDisposalUnitComponent.ContainerId); - UpdateInterface(component, component.Powered); + UpdateInterface(uid, component, component.Powered); - if (!EntityManager.HasComponent(component.Owner)) + if (!HasComp(uid)) { Logger.WarningS("VitalComponentMissing", $"Disposal unit {uid} is missing an {nameof(AnchorableComponent)}"); } @@ -311,10 +311,10 @@ namespace Content.Server.Disposal.Unit.EntitySystems { foreach (var entity in component.Container.ContainedEntities.ToArray()) { - component.Container.ForceRemove(entity); + component.Container.Remove(entity, force: true); } - _ui.TryCloseAll(component.Owner, SharedDisposalUnitComponent.DisposalUnitUiKey.Key); + _ui.TryCloseAll(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key); component.AutomaticEngageToken?.Cancel(); component.AutomaticEngageToken = null; @@ -336,28 +336,28 @@ namespace Content.Server.Disposal.Unit.EntitySystems component.AutomaticEngageToken = null; } - HandleStateChange(component, args.Powered && component.State == SharedDisposalUnitComponent.PressureState.Pressurizing); - UpdateVisualState(component); - UpdateInterface(component, args.Powered); + HandleStateChange(uid, component, args.Powered && component.State == SharedDisposalUnitComponent.PressureState.Pressurizing); + UpdateVisualState(uid, component); + UpdateInterface(uid, component, args.Powered); - if (component.Engaged && !TryFlush(component)) + if (component.Engaged && !TryFlush(uid, component)) { - TryQueueEngage(component); + TryQueueEngage(uid, component); } } /// /// Add or remove this disposal from the active ones for updating. /// - public void HandleStateChange(DisposalUnitComponent component, bool active) + public void HandleStateChange(EntityUid uid, DisposalUnitComponent component, bool active) { if (active) { - EnsureComp(component.Owner); + EnsureComp(uid); } else { - RemComp(component.Owner); + RemComp(uid); } } @@ -365,7 +365,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems { var currentTime = GameTiming.CurTime; - if (!EntityManager.TryGetComponent(args.Entity, out HandsComponent? hands) || + if (!TryComp(args.Entity, out HandsComponent? hands) || hands.Count == 0 || currentTime < component.LastExitAttempt + ExitAttemptDelay) { @@ -373,7 +373,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems } component.LastExitAttempt = currentTime; - Remove(component, args.Entity); + Remove(uid, component, args.Entity); } private void OnAnchorChanged(EntityUid uid, DisposalUnitComponent component, ref AnchorStateChangedEvent args) @@ -381,26 +381,26 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (Terminating(uid)) return; - UpdateVisualState(component); + UpdateVisualState(uid, component); if (!args.Anchored) - TryEjectContents(component); + TryEjectContents(uid, component); } private void HandleDestruction(EntityUid uid, DisposalUnitComponent component, DestructionEventArgs args) { - TryEjectContents(component); + TryEjectContents(uid, component); } private void HandleDragDropOn(EntityUid uid, DisposalUnitComponent component, DragDropEvent args) { - args.Handled = TryInsert(component.Owner, args.Dragged, args.User); + args.Handled = TryInsert(uid, args.Dragged, args.User); } #endregion /// /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding. /// - private bool Update(DisposalUnitComponent component, float frameTime) + private bool Update(EntityUid uid, DisposalUnitComponent component, float frameTime) { var oldPressure = component.Pressure; @@ -411,40 +411,55 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (oldPressure < 1 && state == SharedDisposalUnitComponent.PressureState.Ready) { - UpdateVisualState(component); - UpdateInterface(component, component.Powered); + UpdateVisualState(uid, component); + UpdateInterface(uid, component, component.Powered); if (component.Engaged) { - TryFlush(component); + TryFlush(uid, component); state = component.State; } } + if (component.State == SharedDisposalUnitComponent.PressureState.Pressurizing) + { + var oldTimeElapsed = oldPressure / PressurePerSecond; + if (oldTimeElapsed < component.FlushTime && (oldTimeElapsed + frameTime) >= component.FlushTime) + { + // We've crossed over the amount of time it takes to flush. This will switch the + // visuals over to a 'Charging' state. + UpdateVisualState(uid, component); + } + } + Box2? disposalsBounds = null; var count = component.RecentlyEjected.Count; if (count > 0) { - if (!EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? disposalsBody)) + if (!TryComp(uid, out PhysicsComponent? disposalsBody)) { component.RecentlyEjected.Clear(); } else { - disposalsBounds = _lookup.GetWorldAABB(disposalsBody.Owner); + disposalsBounds = _lookup.GetWorldAABB(uid); } } for (var i = component.RecentlyEjected.Count - 1; i >= 0; i--) { - var uid = component.RecentlyEjected[i]; - if (EntityManager.EntityExists(uid) && - EntityManager.TryGetComponent(uid, out PhysicsComponent? body)) + var ejectedId = component.RecentlyEjected[i]; + if (Exists(ejectedId) && + TryComp(ejectedId, out PhysicsComponent? body)) { // 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. - if (!EntityManager.HasComponent(uid) && _lookup.GetWorldAABB(body.Owner).Intersects(disposalsBounds!.Value)) continue; + if (!HasComp(ejectedId) + && _lookup.GetWorldAABB(ejectedId).Intersects(disposalsBounds!.Value)) + { + continue; + } component.RecentlyEjected.RemoveAt(i); } } @@ -495,30 +510,30 @@ namespace Content.Server.Disposal.Unit.EntitySystems } - public bool TryFlush(DisposalUnitComponent component) + public bool TryFlush(EntityUid uid, DisposalUnitComponent component) { - if (component.Deleted || !CanFlush(component)) + if (component.Deleted || !CanFlush(uid, component)) { return false; } //Allows the MailingUnitSystem to add tags or prevent flushing var beforeFlushArgs = new BeforeDisposalFlushEvent(); - RaiseLocalEvent(component.Owner, beforeFlushArgs); + RaiseLocalEvent(uid, beforeFlushArgs); if (beforeFlushArgs.Cancelled) { - Disengage(component); + Disengage(uid, component); return false; } - var xform = Transform(component.Owner); + var xform = Transform(uid); if (!TryComp(xform.GridUid, out MapGridComponent? grid)) return false; var coords = xform.Coordinates; var entry = grid.GetLocal(coords) - .FirstOrDefault(entity => EntityManager.HasComponent(entity)); + .FirstOrDefault(entity => HasComp(entity)); if (entry == default) { @@ -526,8 +541,8 @@ namespace Content.Server.Disposal.Unit.EntitySystems } var air = component.Air; - var entryComponent = EntityManager.GetComponent(entry); - var indices = _transformSystem.GetGridOrMapTilePosition(component.Owner, xform); + var entryComponent = Comp(entry); + var indices = _transformSystem.GetGridOrMapTilePosition(uid, xform); if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is {Temperature: > 0} environment) { @@ -542,25 +557,25 @@ namespace Content.Server.Disposal.Unit.EntitySystems component.AutomaticEngageToken = null; component.Pressure = 0; - component.State = component.Pressure >= 1 ? SharedDisposalUnitComponent.PressureState.Ready : SharedDisposalUnitComponent.PressureState.Pressurizing; + component.State = SharedDisposalUnitComponent.PressureState.Pressurizing; component.Engaged = false; - HandleStateChange(component, true); - UpdateVisualState(component, true); - UpdateInterface(component, component.Powered); + HandleStateChange(uid, component, true); + UpdateVisualState(uid, component, true); + UpdateInterface(uid, component, component.Powered); return true; } - public void UpdateInterface(DisposalUnitComponent component, bool powered) + public void UpdateInterface(EntityUid uid, DisposalUnitComponent component, bool powered) { var stateString = Loc.GetString($"disposal-unit-state-{component.State}"); - var state = new SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState(Name(component.Owner), stateString, EstimatedFullPressure(component), powered, component.Engaged); - _ui.TrySetUiState(component.Owner, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, state); + var state = new SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState(Name(uid), stateString, EstimatedFullPressure(component), powered, component.Engaged); + _ui.TrySetUiState(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, state); var stateUpdatedEvent = new DisposalUnitUIStateUpdatedEvent(state); - RaiseLocalEvent(component.Owner, stateUpdatedEvent); + RaiseLocalEvent(uid, stateUpdatedEvent); } private TimeSpan EstimatedFullPressure(DisposalUnitComponent component) @@ -573,59 +588,64 @@ namespace Content.Server.Disposal.Unit.EntitySystems return TimeSpan.FromSeconds(currentTime.TotalSeconds + (1.0f - pressure) / PressurePerSecond); } - public void UpdateVisualState(DisposalUnitComponent component) + public void UpdateVisualState(EntityUid uid, DisposalUnitComponent component) { - UpdateVisualState(component, false); + UpdateVisualState(uid, component, false); } - public void UpdateVisualState(DisposalUnitComponent component, bool flush) + public void UpdateVisualState(EntityUid uid, DisposalUnitComponent component, bool flush) { - if (!EntityManager.TryGetComponent(component.Owner, out AppearanceComponent? appearance)) + if (!TryComp(uid, out AppearanceComponent? appearance)) { return; } - if (!EntityManager.GetComponent(component.Owner).Anchored) + if (!Comp(uid).Anchored) { - _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); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.UnAnchored, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, SharedDisposalUnitComponent.HandleState.Normal, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance); return; } - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.VisualState, component.Pressure < 1 ? SharedDisposalUnitComponent.VisualState.Charging : SharedDisposalUnitComponent.VisualState.Anchored, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, component.Pressure < 1 ? SharedDisposalUnitComponent.VisualState.Charging : SharedDisposalUnitComponent.VisualState.Anchored, appearance); - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.Handle, component.Engaged + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, component.Engaged ? SharedDisposalUnitComponent.HandleState.Engaged : SharedDisposalUnitComponent.HandleState.Normal, appearance); if (!component.Powered) { - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightState.Off, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance); return; } + var lightState = SharedDisposalUnitComponent.LightStates.Off; if (flush) { - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Flushing, appearance); - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightState.Off, appearance); - return; + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Flushing, appearance); } if (component.Container.ContainedEntities.Count > 0) { - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightState.Full, appearance); - return; + lightState |= SharedDisposalUnitComponent.LightStates.Full; } - _appearance.SetData(component.Owner, SharedDisposalUnitComponent.Visuals.Light, component.Pressure < 1 - ? SharedDisposalUnitComponent.LightState.Charging - : SharedDisposalUnitComponent.LightState.Ready, appearance); + if (component.Pressure < 1) + { + lightState |= SharedDisposalUnitComponent.LightStates.Charging; + } + else if (!component.Engaged) + { + lightState |= SharedDisposalUnitComponent.LightStates.Ready; + } + + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, lightState, appearance); } - public void Remove(DisposalUnitComponent component, EntityUid entity) + public void Remove(EntityUid uid, DisposalUnitComponent component, EntityUid toRemove) { - component.Container.Remove(entity); + component.Container.Remove(toRemove); if (component.Container.ContainedEntities.Count == 0) { @@ -633,46 +653,48 @@ namespace Content.Server.Disposal.Unit.EntitySystems component.AutomaticEngageToken = null; } - if (!component.RecentlyEjected.Contains(entity)) - component.RecentlyEjected.Add(entity); + if (!component.RecentlyEjected.Contains(toRemove)) + component.RecentlyEjected.Add(toRemove); Dirty(component); - HandleStateChange(component, true); - UpdateVisualState(component); + HandleStateChange(uid, component, active: true); + UpdateVisualState(uid, component); } - public bool CanFlush(DisposalUnitComponent component) + public bool CanFlush(EntityUid unit, DisposalUnitComponent component) { - return component.State == SharedDisposalUnitComponent.PressureState.Ready && component.Powered && EntityManager.GetComponent(component.Owner).Anchored; + return component.State == SharedDisposalUnitComponent.PressureState.Ready + && component.Powered + && Comp(unit).Anchored; } - public void Engage(DisposalUnitComponent component) + public void Engage(EntityUid uid, DisposalUnitComponent component) { component.Engaged = true; - UpdateVisualState(component); - UpdateInterface(component, component.Powered); + UpdateVisualState(uid, component); + UpdateInterface(uid, component, component.Powered); - if (CanFlush(component)) + if (CanFlush(uid, component)) { - component.Owner.SpawnTimer(component.FlushDelay, () => TryFlush(component)); + uid.SpawnTimer(component.FlushDelay, () => TryFlush(uid, component)); } } - public void Disengage(DisposalUnitComponent component) + public void Disengage(EntityUid uid, DisposalUnitComponent component) { component.Engaged = false; - UpdateVisualState(component); - UpdateInterface(component, component.Powered); + UpdateVisualState(uid, component); + UpdateInterface(uid, component, component.Powered); } /// /// Remove all entities currently in the disposal unit. /// - public void TryEjectContents(DisposalUnitComponent component) + public void TryEjectContents(EntityUid uid, DisposalUnitComponent component) { foreach (var entity in component.Container.ContainedEntities.ToArray()) { - Remove(component, entity); + Remove(uid, component, entity); } } @@ -687,7 +709,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems /// /// If something is inserted (or the likes) then we'll queue up a flush in the future. /// - public void TryQueueEngage(DisposalUnitComponent component) + public void TryQueueEngage(EntityUid uid, DisposalUnitComponent component) { if (component.Deleted || !component.AutomaticEngage || !component.Powered && component.Container.ContainedEntities.Count == 0) { @@ -696,25 +718,25 @@ namespace Content.Server.Disposal.Unit.EntitySystems component.AutomaticEngageToken = new CancellationTokenSource(); - component.Owner.SpawnTimer(component.AutomaticEngageTime, () => + uid.SpawnTimer(component.AutomaticEngageTime, () => { - if (!TryFlush(component)) + if (!TryFlush(uid, component)) { - TryQueueEngage(component); + TryQueueEngage(uid, component); } }, component.AutomaticEngageToken.Token); } - public void AfterInsert(DisposalUnitComponent component, EntityUid entity) + public void AfterInsert(EntityUid uid, DisposalUnitComponent component, EntityUid inserted) { - TryQueueEngage(component); + TryQueueEngage(uid, component); - if (EntityManager.TryGetComponent(entity, out ActorComponent? actor)) + if (TryComp(inserted, out ActorComponent? actor)) { - _ui.TryClose(component.Owner, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession); + _ui.TryClose(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession); } - UpdateVisualState(component); + UpdateVisualState(uid, component); } } diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs index 2840ddf3c3..581332b47f 100644 --- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs +++ b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs @@ -15,6 +15,8 @@ namespace Content.Shared.Disposal.Components /// public List RecentlyEjected = new(); + [DataField("flushTime", required: true)] + public readonly float FlushTime; [DataField("mobsCanEnter")] public bool MobsCanEnter = true; @@ -44,12 +46,12 @@ namespace Content.Shared.Disposal.Components } [Serializable, NetSerializable] - public enum LightState : byte + public enum LightStates : byte { - Off, - Charging, - Full, - Ready + Off = 0, + Charging = 1 << 0, + Full = 1 << 1, + Ready = 1 << 2 } [Serializable, NetSerializable] diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 139c08d32f..6610be17d1 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -14,11 +14,21 @@ snapCardinals: true layers: - state: condisposal - map: ["enum.DisposalUnitVisualLayers.Base"] - - state: dispover-handle - map: ["enum.DisposalUnitVisualLayers.Handle"] + map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] + - state: disposal + map: [ "enum.DisposalUnitVisualLayers.Base" ] + - state: disposal-charging + map: [ "enum.DisposalUnitVisualLayers.BaseCharging" ] + - state: disposal-flush + map: [ "enum.DisposalUnitVisualLayers.BaseFlush" ] + - state: dispover-charge + map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] - state: dispover-ready - map: ["enum.DisposalUnitVisualLayers.Light"] + map: [ "enum.DisposalUnitVisualLayers.OverlayReady" ] + - state: dispover-full + map: [ "enum.DisposalUnitVisualLayers.OverlayFull" ] + - state: dispover-handle + map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] - type: Physics bodyType: Static - type: Fixtures @@ -54,19 +64,6 @@ min: 1 max: 1 - type: Appearance - visuals: - - type: DisposalUnitVisualizer - state_unanchored: condisposal - state_anchored: disposal - state_charging: disposal-charging - overlay_charging: dispover-charge - overlay_ready: dispover-ready - overlay_full: dispover-full - overlay_engaged: dispover-handle - state_flush: disposal-flush - flush_sound: - path: /Audio/Machines/disposalflush.ogg - flush_time: 2 - type: UserInterface interfaces: - key: enum.DisposalUnitUiKey.Key @@ -87,6 +84,8 @@ graph: DisposalMachine node: disposal_unit - type: DisposalUnit + flushSound: + path: /Audio/Machines/disposalflush.ogg flushTime: 2 - type: UserInterface interfaces: @@ -104,18 +103,30 @@ sprite: Structures/Piping/disposal.rsi layers: - state: conmailing + map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] + - state: mailing map: [ "enum.DisposalUnitVisualLayers.Base" ] - - state: mailover-handle - map: [ "enum.DisposalUnitVisualLayers.Handle" ] + - state: mailing-charging + map: [ "enum.DisposalUnitVisualLayers.BaseCharging" ] + - state: mailing-flush + map: [ "enum.DisposalUnitVisualLayers.BaseFlush" ] + - state: dispover-charge + map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] - state: dispover-ready - map: [ "enum.DisposalUnitVisualLayers.Light" ] + map: [ "enum.DisposalUnitVisualLayers.OverlayReady" ] + - state: dispover-full + map: [ "enum.DisposalUnitVisualLayers.OverlayFull" ] + - state: mailover-handle + map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] - type: Construction graph: DisposalMachine node: mailing_unit - type: DisposalUnit - flushTime: 2 autoEngageEnabled: false mobsCanEnter: false + flushSound: + path: /Audio/Machines/disposalflush.ogg + flushTime: 2 - type: MailingUnit - type: DeviceNetwork deviceNetId: Wired @@ -124,19 +135,6 @@ - type: WiredNetworkConnection - type: Configuration - type: Appearance - visuals: - - type: DisposalUnitVisualizer - state_unanchored: conmailing - state_anchored: mailing - state_charging: mailing-charging - overlay_charging: dispover-charge - overlay_ready: dispover-ready - overlay_full: dispover-full - overlay_engaged: mailover-handle - state_flush: mailing-flush - flush_sound: - path: /Audio/Machines/disposalflush.ogg - flush_time: 2 - type: UserInterface interfaces: - key: enum.MailingUnitUiKey.Key