From 5edf2ccad5c5e2619a5a7f85b375aad9c6a61384 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 13 Jul 2022 19:11:59 -0400 Subject: [PATCH] EntityStorage ECS (#9291) --- .../Morgue/Visualizers/BodyBagVisualLayers.cs | 6 + .../Morgue/Visualizers/BodyBagVisualizer.cs | 37 -- .../Visualizers/CrematoriumVisualizer.cs | 63 --- .../CrematoriumVisualizerSystem.cs | 36 ++ .../CrematoriumVisualsComponent.cs | 16 + .../Morgue/Visualizers/MorgueVisualizer.cs | 64 --- .../Visualizers/MorgueVisualizerSystem.cs | 37 ++ .../Visualizers/MorgueVisualsComponent.cs | 18 + .../Storage/Visualizers/StorageVisualizer.cs | 30 +- .../Tests/ContainerOcclusionTest.cs | 12 +- .../Inventory/CloseStorageOperator.cs | 12 +- .../Inventory/OpenStorageOperator.cs | 4 +- .../Commands/AddEntityStorageCommand.cs | 6 +- .../Commands/RemoveEntityStorageCommand.cs | 4 +- Content.Server/Entry/IgnoredComponents.cs | 2 + Content.Server/Foldable/FoldableSystem.cs | 6 +- Content.Server/Lock/LockSystem.cs | 13 + .../BodyBagEntityStorageComponent.cs | 20 - .../Morgue/Components/CrematoriumComponent.cs | 31 ++ .../CrematoriumEntityStorageComponent.cs | 98 ---- ...ntityStorageLayingDownOverrideComponent.cs | 7 + .../Morgue/Components/MorgueComponent.cs | 26 + .../MorgueEntityStorageComponent.cs | 173 ------- .../Morgue/Components/MorgueTrayComponent.cs | 22 - Content.Server/Morgue/CrematoriumSystem.cs | 162 ++++++ .../EntityStorageLayingDownOverrideSystem.cs | 25 + Content.Server/Morgue/MorgueSystem.cs | 188 +++---- Content.Server/Resist/ResistLockerSystem.cs | 4 +- .../Components/ArtifactStorageComponent.cs | 9 +- .../CursedEntityStorageComponent.cs | 54 +- .../Components/EntityStorageComponent.cs | 477 ++++-------------- .../Storage/Components/IStorageComponent.cs | 9 - .../EntitySystems/ArtifactStorageSystem.cs | 23 + .../CursedEntityStorageSystem.cs | 47 ++ .../EntitySystems/EntityStorageSystem.cs | 306 ++++++++++- .../EntitySystems/StorageSystem.Fill.cs | 10 +- .../Storage/EntitySystems/StorageSystem.cs | 10 +- .../SurplusBundle/SurplusBundleSystem.cs | 11 +- Content.Shared/Morgue/SharedMorgue.cs | 29 +- .../Storage/SharedStorageComponent.cs | 1 + .../morgue-entity-storage-component.ftl | 1 - .../components/entity-storage-component.ftl | 1 + .../Objects/Specific/Medical/morgue.yml | 10 +- .../Xenoarchaeology/artifact_equipment.yml | 3 +- .../Structures/Storage/Closets/cursed.yml | 3 + .../Entities/Structures/Storage/morgue.yml | 57 ++- 46 files changed, 1057 insertions(+), 1126 deletions(-) create mode 100644 Content.Client/Morgue/Visualizers/BodyBagVisualLayers.cs delete mode 100644 Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs delete mode 100644 Content.Client/Morgue/Visualizers/CrematoriumVisualizer.cs create mode 100644 Content.Client/Morgue/Visualizers/CrematoriumVisualizerSystem.cs create mode 100644 Content.Client/Morgue/Visualizers/CrematoriumVisualsComponent.cs delete mode 100644 Content.Client/Morgue/Visualizers/MorgueVisualizer.cs create mode 100644 Content.Client/Morgue/Visualizers/MorgueVisualizerSystem.cs create mode 100644 Content.Client/Morgue/Visualizers/MorgueVisualsComponent.cs delete mode 100644 Content.Server/Morgue/Components/BodyBagEntityStorageComponent.cs create mode 100644 Content.Server/Morgue/Components/CrematoriumComponent.cs delete mode 100644 Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs create mode 100644 Content.Server/Morgue/Components/EntityStorageLayingDownOverrideComponent.cs create mode 100644 Content.Server/Morgue/Components/MorgueComponent.cs delete mode 100644 Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs delete mode 100644 Content.Server/Morgue/Components/MorgueTrayComponent.cs create mode 100644 Content.Server/Morgue/CrematoriumSystem.cs create mode 100644 Content.Server/Morgue/EntityStorageLayingDownOverrideSystem.cs delete mode 100644 Content.Server/Storage/Components/IStorageComponent.cs create mode 100644 Content.Server/Storage/EntitySystems/ArtifactStorageSystem.cs create mode 100644 Content.Server/Storage/EntitySystems/CursedEntityStorageSystem.cs diff --git a/Content.Client/Morgue/Visualizers/BodyBagVisualLayers.cs b/Content.Client/Morgue/Visualizers/BodyBagVisualLayers.cs new file mode 100644 index 0000000000..3fe5dcf13a --- /dev/null +++ b/Content.Client/Morgue/Visualizers/BodyBagVisualLayers.cs @@ -0,0 +1,6 @@ +namespace Content.Client.Morgue.Visualizers; + +public enum BodyBagVisualLayers : byte +{ + Label, +} diff --git a/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs b/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs deleted file mode 100644 index b64ce7d8d8..0000000000 --- a/Content.Client/Morgue/Visualizers/BodyBagVisualizer.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Content.Shared.Labels; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; - -namespace Content.Client.Morgue.Visualizers -{ - [UsedImplicitly] - public sealed class BodyBagVisualizer : AppearanceVisualizer - { - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var entities = IoCManager.Resolve(); - if (!entities.TryGetComponent(component.Owner, out ISpriteComponent? sprite)) - { - return; - } - - if (component.TryGetData(PaperLabelVisuals.HasLabel, out bool labelVal)) - { - sprite.LayerSetVisible(BodyBagVisualLayers.Label, labelVal); - } - else - { - sprite.LayerSetVisible(BodyBagVisualLayers.Label, false); - } - } - } - - public enum BodyBagVisualLayers : byte - { - Label, - } -} diff --git a/Content.Client/Morgue/Visualizers/CrematoriumVisualizer.cs b/Content.Client/Morgue/Visualizers/CrematoriumVisualizer.cs deleted file mode 100644 index de39fedbb4..0000000000 --- a/Content.Client/Morgue/Visualizers/CrematoriumVisualizer.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Content.Shared.Morgue; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Client.Morgue.Visualizers -{ - [UsedImplicitly] - public sealed class CrematoriumVisualizer : AppearanceVisualizer - { - [DataField("state_open")] - private string _stateOpen = ""; - [DataField("state_closed")] - private string _stateClosed = ""; - - [DataField("light_contents")] - private string _lightContents = ""; - [DataField("light_burning")] - private string _lightBurning = ""; - - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var entities = IoCManager.Resolve(); - if (!entities.TryGetComponent(component.Owner, out ISpriteComponent? sprite)) - { - return; - } - - if (component.TryGetData(MorgueVisuals.Open, out bool open)) - { - sprite.LayerSetState(CrematoriumVisualLayers.Base, open ? _stateOpen : _stateClosed); - } - else - { - sprite.LayerSetState(CrematoriumVisualLayers.Base, _stateClosed); - } - - var lightState = ""; - if (component.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) lightState = _lightContents; - if (component.TryGetData(CrematoriumVisuals.Burning, out bool isBurning) && isBurning) lightState = _lightBurning; - - if (!string.IsNullOrEmpty(lightState)) - { - sprite.LayerSetState(CrematoriumVisualLayers.Light, lightState); - sprite.LayerSetVisible(CrematoriumVisualLayers.Light, true); - } - else - { - sprite.LayerSetVisible(CrematoriumVisualLayers.Light, false); - } - } - } - - public enum CrematoriumVisualLayers : byte - { - Base, - Light, - } -} diff --git a/Content.Client/Morgue/Visualizers/CrematoriumVisualizerSystem.cs b/Content.Client/Morgue/Visualizers/CrematoriumVisualizerSystem.cs new file mode 100644 index 0000000000..a63ec062a8 --- /dev/null +++ b/Content.Client/Morgue/Visualizers/CrematoriumVisualizerSystem.cs @@ -0,0 +1,36 @@ +using Content.Shared.Morgue; +using Content.Shared.Storage; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.Morgue.Visualizers; + +public sealed class CrematoriumVisualizerSystem : VisualizerSystem +{ + public override void Initialize() + { + base.Initialize(); + } + + protected override void OnAppearanceChange(EntityUid uid, CrematoriumVisualsComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + string? lightState = null; + if (args.Component.TryGetData(CrematoriumVisuals.Burning, out bool isBurning) && isBurning) + lightState = component.LightBurning; + else if (args.Component.TryGetData(StorageVisuals.HasContents, out bool hasContents) && hasContents) + lightState = component.LightContents; + + if (lightState != null) + { + args.Sprite.LayerSetState(CrematoriumVisualLayers.Light, lightState); + args.Sprite.LayerSetVisible(CrematoriumVisualLayers.Light, true); + } + else + { + args.Sprite.LayerSetVisible(CrematoriumVisualLayers.Light, false); + } + } +} diff --git a/Content.Client/Morgue/Visualizers/CrematoriumVisualsComponent.cs b/Content.Client/Morgue/Visualizers/CrematoriumVisualsComponent.cs new file mode 100644 index 0000000000..b2bb026294 --- /dev/null +++ b/Content.Client/Morgue/Visualizers/CrematoriumVisualsComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Client.Morgue.Visualizers; + +[RegisterComponent] +public sealed class CrematoriumVisualsComponent : Component +{ + [DataField("lightContents", required: true)] + public string LightContents = default!; + [DataField("lightBurning", required: true)] + public string LightBurning = default!; +} + +public enum CrematoriumVisualLayers : byte +{ + Base, + Light, +} diff --git a/Content.Client/Morgue/Visualizers/MorgueVisualizer.cs b/Content.Client/Morgue/Visualizers/MorgueVisualizer.cs deleted file mode 100644 index 6f675a1135..0000000000 --- a/Content.Client/Morgue/Visualizers/MorgueVisualizer.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Content.Shared.Morgue; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Client.Morgue.Visualizers -{ - public sealed class MorgueVisualizer : AppearanceVisualizer - { - [DataField("state_open")] - private string _stateOpen = ""; - [DataField("state_closed")] - private string _stateClosed = ""; - - [DataField("light_contents")] - private string _lightContents = ""; - [DataField("light_mob")] - private string _lightMob = ""; - [DataField("light_soul")] - private string _lightSoul = ""; - - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var entities = IoCManager.Resolve(); - if (!entities.TryGetComponent(component.Owner, out ISpriteComponent? sprite)) - { - return; - } - - if (component.TryGetData(MorgueVisuals.Open, out bool open)) - { - sprite.LayerSetState(MorgueVisualLayers.Base, open ? _stateOpen : _stateClosed); - } - else - { - sprite.LayerSetState(MorgueVisualLayers.Base, _stateClosed); - } - - var lightState = ""; - if (component.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) lightState = _lightContents; - if (component.TryGetData(MorgueVisuals.HasMob, out bool hasMob) && hasMob) lightState = _lightMob; - if (component.TryGetData(MorgueVisuals.HasSoul, out bool hasSoul) && hasSoul) lightState = _lightSoul; - - if (!string.IsNullOrEmpty(lightState)) - { - sprite.LayerSetState(MorgueVisualLayers.Light, lightState); - sprite.LayerSetVisible(MorgueVisualLayers.Light, true); - } - else - { - sprite.LayerSetVisible(MorgueVisualLayers.Light, false); - } - } - } - - public enum MorgueVisualLayers : byte - { - Base, - Light, - } -} diff --git a/Content.Client/Morgue/Visualizers/MorgueVisualizerSystem.cs b/Content.Client/Morgue/Visualizers/MorgueVisualizerSystem.cs new file mode 100644 index 0000000000..914fc00903 --- /dev/null +++ b/Content.Client/Morgue/Visualizers/MorgueVisualizerSystem.cs @@ -0,0 +1,37 @@ +using Content.Shared.Morgue; +using Content.Shared.Storage; +using Robust.Client.GameObjects; + +namespace Content.Client.Morgue.Visualizers; + +public sealed class MorgueVisualizerSystem : VisualizerSystem +{ + public override void Initialize() + { + base.Initialize(); + } + + protected override void OnAppearanceChange(EntityUid uid, MorgueVisualsComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + string? lightState = null; + if (args.Component.TryGetData(MorgueVisuals.HasSoul, out bool hasSoul) && hasSoul) + lightState = component.LightSoul; + else if (args.Component.TryGetData(MorgueVisuals.HasMob, out bool hasMob) && hasMob) + lightState = component.LightMob; + else if (args.Component.TryGetData(StorageVisuals.HasContents, out bool hasContents) && hasContents) + lightState = component.LightContents; + + if (lightState != null) + { + args.Sprite.LayerSetState(MorgueVisualLayers.Light, lightState); + args.Sprite.LayerSetVisible(MorgueVisualLayers.Light, true); + } + else + { + args.Sprite.LayerSetVisible(MorgueVisualLayers.Light, false); + } + } +} diff --git a/Content.Client/Morgue/Visualizers/MorgueVisualsComponent.cs b/Content.Client/Morgue/Visualizers/MorgueVisualsComponent.cs new file mode 100644 index 0000000000..7e58c33821 --- /dev/null +++ b/Content.Client/Morgue/Visualizers/MorgueVisualsComponent.cs @@ -0,0 +1,18 @@ +namespace Content.Client.Morgue.Visualizers; + +[RegisterComponent] +public sealed class MorgueVisualsComponent : Component +{ + [DataField("lightContents", required: true)] + public string LightContents = default!; + [DataField("lightMob", required: true)] + public string LightMob = default!; + [DataField("lightSoul", required: true)] + public string LightSoul = default!; +} + +public enum MorgueVisualLayers : byte +{ + Base, + Light, +} diff --git a/Content.Client/Storage/Visualizers/StorageVisualizer.cs b/Content.Client/Storage/Visualizers/StorageVisualizer.cs index 0b8216eb40..124d3843b8 100644 --- a/Content.Client/Storage/Visualizers/StorageVisualizer.cs +++ b/Content.Client/Storage/Visualizers/StorageVisualizer.cs @@ -15,6 +15,8 @@ namespace Content.Client.Storage.Visualizers /// [DataField("state")] private string? _stateBase; + [DataField("state_alt")] + private string? _stateBaseAlt; [DataField("state_open")] private string? _stateOpen; [DataField("state_closed")] @@ -31,6 +33,11 @@ namespace Content.Client.Storage.Visualizers { sprite.LayerSetState(0, _stateBase); } + + if (_stateBaseAlt == null) + { + _stateBaseAlt = _stateBase; + } } public override void OnChangeData(AppearanceComponent component) @@ -49,13 +56,28 @@ namespace Content.Client.Storage.Visualizers { sprite.LayerSetVisible(StorageVisualLayers.Door, true); - if (open && _stateOpen != null) + if (open) { - sprite.LayerSetState(StorageVisualLayers.Door, _stateOpen); + if (_stateOpen != null) + { + sprite.LayerSetState(StorageVisualLayers.Door, _stateOpen); + sprite.LayerSetVisible(StorageVisualLayers.Door, true); + } + + if (_stateBaseAlt != null) + sprite.LayerSetState(0, _stateBaseAlt); } - else if (!open && _stateClosed != null) + else if (!open) { - sprite.LayerSetState(StorageVisualLayers.Door, _stateClosed); + if (_stateClosed != null) + sprite.LayerSetState(StorageVisualLayers.Door, _stateClosed); + else + { + sprite.LayerSetVisible(StorageVisualLayers.Door, false); + } + + if (_stateBase != null) + sprite.LayerSetState(0, _stateBase); } else { diff --git a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs index 421cfae30a..fc69394913 100644 --- a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs +++ b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using NUnit.Framework; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -49,10 +50,11 @@ namespace Content.IntegrationTests.Tests var mapId = ent2.GetAllMapIds().Last(); var pos = new MapCoordinates(Vector2.Zero, mapId); var ent = IoCManager.Resolve(); + var entStorage = ent.EntitySysManager.GetEntitySystem(); var container = ent.SpawnEntity("ContainerOcclusionA", pos); dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos); - ent.GetComponent(container).Insert(dummy); + entStorage.Insert(dummy, container); }); await PoolManager.RunTicksSync(pairTracker.Pair, 5); @@ -84,10 +86,11 @@ namespace Content.IntegrationTests.Tests var mapId = ent2.GetAllMapIds().Last(); var pos = new MapCoordinates(Vector2.Zero, mapId); var ent = IoCManager.Resolve(); + var entStorage = ent.EntitySysManager.GetEntitySystem(); var container = ent.SpawnEntity("ContainerOcclusionB", pos); dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos); - ent.GetComponent(container).Insert(dummy); + entStorage.Insert(dummy, container); }); await PoolManager.RunTicksSync(pairTracker.Pair, 5); @@ -119,12 +122,13 @@ namespace Content.IntegrationTests.Tests var mapId = ent2.GetAllMapIds().Last(); var pos = new MapCoordinates(Vector2.Zero, mapId); var ent = IoCManager.Resolve(); + var entStorage = ent.EntitySysManager.GetEntitySystem(); var containerA = ent.SpawnEntity("ContainerOcclusionA", pos); var containerB = ent.SpawnEntity("ContainerOcclusionB", pos); dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos); - ent.GetComponent(containerA).Insert(containerB); - ent.GetComponent(containerB).Insert(dummy); + entStorage.Insert(dummy, containerA); + entStorage.Insert(dummy, containerA); }); await PoolManager.RunTicksSync(pairTracker.Pair, 5); diff --git a/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs b/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs index 84f72a1227..69454e6554 100644 --- a/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs +++ b/Content.Server/AI/Operators/Inventory/CloseStorageOperator.cs @@ -1,6 +1,7 @@ using Content.Server.AI.Utility; using Content.Server.AI.WorldState.States.Inventory; using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using Content.Shared.Interaction; namespace Content.Server.AI.Operators.Inventory @@ -56,16 +57,17 @@ namespace Content.Server.AI.Operators.Inventory return Outcome.Failed; } - if (!IoCManager.Resolve().TryGetComponent(_target, out EntityStorageComponent? storageComponent) || + var entMan = IoCManager.Resolve(); + + if (!entMan.TryGetComponent(_target, out EntityStorageComponent? storageComponent) || storageComponent.IsWeldedShut) { return Outcome.Failed; } - - if (storageComponent.Open) + + if (entMan.EntitySysManager.TryGetEntitySystem(out var entStorage) && storageComponent.Open) { - var activateArgs = new ActivateEventArgs(_owner, _target); - storageComponent.Activate(activateArgs); + entStorage.ToggleOpen(_owner, _target, storageComponent); } return Outcome.Success; diff --git a/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs b/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs index 965c2253fa..2e09501444 100644 --- a/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs +++ b/Content.Server/AI/Operators/Inventory/OpenStorageOperator.cs @@ -1,6 +1,7 @@ using Content.Server.AI.Utility; using Content.Server.AI.WorldState.States.Inventory; using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using Content.Shared.Interaction; using Robust.Shared.Containers; @@ -40,8 +41,7 @@ namespace Content.Server.AI.Operators.Inventory if (!storageComponent.Open) { - var activateArgs = new ActivateEventArgs(_owner, _target); - storageComponent.Activate(activateArgs); + IoCManager.Resolve().ToggleOpen(_owner, _target, storageComponent); } var blackboard = UtilityAiHelpers.GetBlackboard(_owner); diff --git a/Content.Server/Administration/Commands/AddEntityStorageCommand.cs b/Content.Server/Administration/Commands/AddEntityStorageCommand.cs index db07f257d2..f41b40bf9c 100644 --- a/Content.Server/Administration/Commands/AddEntityStorageCommand.cs +++ b/Content.Server/Administration/Commands/AddEntityStorageCommand.cs @@ -1,4 +1,5 @@ using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using Content.Shared.Administration; using Robust.Shared.Console; @@ -33,9 +34,10 @@ namespace Content.Server.Administration.Commands var entityManager = IoCManager.Resolve(); - if (entityManager.TryGetComponent(storageUid, out var storage)) + if (entityManager.HasComponent(storageUid) && + entityManager.EntitySysManager.TryGetEntitySystem(out var storageSys)) { - storage.Insert(entityUid); + storageSys.Insert(entityUid, storageUid); } else { diff --git a/Content.Server/Administration/Commands/RemoveEntityStorageCommand.cs b/Content.Server/Administration/Commands/RemoveEntityStorageCommand.cs index fccdc4a854..3213572cb2 100644 --- a/Content.Server/Administration/Commands/RemoveEntityStorageCommand.cs +++ b/Content.Server/Administration/Commands/RemoveEntityStorageCommand.cs @@ -1,4 +1,5 @@ using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using Content.Shared.Administration; using Robust.Shared.Console; @@ -27,13 +28,14 @@ namespace Content.Server.Administration.Commands var entityManager = IoCManager.Resolve(); + if (!entityManager.EntitySysManager.TryGetEntitySystem(out var entstorage)) return; if (!entityManager.TryGetComponent(entityUid, out var transform)) return; var parent = transform.ParentUid; if (entityManager.TryGetComponent(parent, out var storage)) { - storage.Remove(entityUid); + entstorage.Remove(entityUid, storage.Owner, storage); } else { diff --git a/Content.Server/Entry/IgnoredComponents.cs b/Content.Server/Entry/IgnoredComponents.cs index b5530768be..01e1359458 100644 --- a/Content.Server/Entry/IgnoredComponents.cs +++ b/Content.Server/Entry/IgnoredComponents.cs @@ -36,6 +36,8 @@ namespace Content.Server.Entry "AMEShieldingVisuals", "PipeColorVisuals", "FireVisuals", + "MorgueVisuals", + "CrematoriumVisuals", }; } } diff --git a/Content.Server/Foldable/FoldableSystem.cs b/Content.Server/Foldable/FoldableSystem.cs index c080b188b2..d24ffb7f6a 100644 --- a/Content.Server/Foldable/FoldableSystem.cs +++ b/Content.Server/Foldable/FoldableSystem.cs @@ -19,7 +19,7 @@ namespace Content.Server.Foldable SubscribeLocalEvent(OnFoldableOpenAttempt); SubscribeLocalEvent>(AddFoldVerb); - SubscribeLocalEvent(OnStoreThisAttempt); + SubscribeLocalEvent(OnStoreThisAttempt); } @@ -88,8 +88,10 @@ namespace Content.Server.Foldable strap.Enabled = !component.IsFolded; } - public void OnStoreThisAttempt(EntityUid uid, FoldableComponent comp, StoreThisAttemptEvent args) + public void OnStoreThisAttempt(EntityUid uid, FoldableComponent comp, StoreMobInItemContainerAttemptEvent args) { + args.Handled = true; + if (comp.IsFolded) args.Cancel(); } diff --git a/Content.Server/Lock/LockSystem.cs b/Content.Server/Lock/LockSystem.cs index 172e4def21..ad1b5fbd5f 100644 --- a/Content.Server/Lock/LockSystem.cs +++ b/Content.Server/Lock/LockSystem.cs @@ -20,6 +20,7 @@ namespace Content.Server.Lock public sealed class LockSystem : EntitySystem { [Dependency] private readonly AccessReaderSystem _accessReader = default!; + [Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!; /// public override void Initialize() @@ -27,6 +28,7 @@ namespace Content.Server.Lock base.Initialize(); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnActivated); + SubscribeLocalEvent(OnStorageOpenAttempt); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent>(AddToggleLockVerb); SubscribeLocalEvent(OnEmagged); @@ -58,6 +60,17 @@ namespace Content.Server.Lock } } + private void OnStorageOpenAttempt(EntityUid uid, LockComponent component, StorageOpenAttemptEvent args) + { + if (component.Locked) + { + if (!args.Silent) + _sharedPopupSystem.PopupEntity(Loc.GetString("entity-storage-component-locked-message"), uid, Filter.Pvs(uid)); + + args.Cancel(); + } + } + private void OnExamined(EntityUid uid, LockComponent lockComp, ExaminedEvent args) { args.PushText(Loc.GetString(lockComp.Locked diff --git a/Content.Server/Morgue/Components/BodyBagEntityStorageComponent.cs b/Content.Server/Morgue/Components/BodyBagEntityStorageComponent.cs deleted file mode 100644 index e53acb7a1b..0000000000 --- a/Content.Server/Morgue/Components/BodyBagEntityStorageComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Server.Storage.Components; -using Content.Shared.Body.Components; -using Content.Shared.Interaction; -using Content.Shared.Standing; - -namespace Content.Server.Morgue.Components -{ - [RegisterComponent] - [ComponentReference(typeof(EntityStorageComponent))] - [ComponentReference(typeof(IActivate))] - [ComponentReference(typeof(IStorageComponent))] - public sealed class BodyBagEntityStorageComponent : EntityStorageComponent - { - protected override bool AddToContents(EntityUid entity) - { - if (IoCManager.Resolve().HasComponent(entity) && !EntitySystem.Get().IsDown(entity)) return false; - return base.AddToContents(entity); - } - } -} diff --git a/Content.Server/Morgue/Components/CrematoriumComponent.cs b/Content.Server/Morgue/Components/CrematoriumComponent.cs new file mode 100644 index 0000000000..b34e3f987f --- /dev/null +++ b/Content.Server/Morgue/Components/CrematoriumComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.Sound; +using System.Threading; + +namespace Content.Server.Morgue.Components; + +[RegisterComponent] +public sealed class CrematoriumComponent : Component +{ + /// + /// Whether or not the crematorium is currently cooking + /// + [ViewVariables] + public bool Cooking; + + /// + /// The time it takes to cook + /// + [ViewVariables(VVAccess.ReadWrite)] + public int BurnMilis = 5000; + + public CancellationTokenSource? CremateCancelToken; + + [DataField("cremateStartSound")] + public SoundSpecifier CremateStartSound = new SoundPathSpecifier("/Audio/Items/lighter1.ogg"); + + [DataField("crematingSound")] + public SoundSpecifier CrematingSound = new SoundPathSpecifier("/Audio/Effects/burning.ogg"); + + [DataField("cremateFinishSound")] + public SoundSpecifier CremateFinishSound = new SoundPathSpecifier("/Audio/Machines/ding.ogg"); +} diff --git a/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs b/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs deleted file mode 100644 index 668342d5c5..0000000000 --- a/Content.Server/Morgue/Components/CrematoriumEntityStorageComponent.cs +++ /dev/null @@ -1,98 +0,0 @@ - -using System.Threading; -using Content.Server.Storage.Components; -using Content.Shared.Interaction; -using Content.Shared.Morgue; -using Content.Shared.Popups; -using Content.Shared.Sound; -using Robust.Shared.Audio; -using Robust.Shared.Player; - -namespace Content.Server.Morgue.Components -{ - [RegisterComponent] - [ComponentReference(typeof(MorgueEntityStorageComponent))] - [ComponentReference(typeof(EntityStorageComponent))] - [ComponentReference(typeof(IActivate))] - [ComponentReference(typeof(IStorageComponent))] -#pragma warning disable 618 - public sealed class CrematoriumEntityStorageComponent : MorgueEntityStorageComponent -#pragma warning restore 618 - { - [Dependency] private readonly IEntityManager _entities = default!; - [DataField("cremateStartSound")] private SoundSpecifier _cremateStartSound = new SoundPathSpecifier("/Audio/Items/lighter1.ogg"); - [DataField("crematingSound")] private SoundSpecifier _crematingSound = new SoundPathSpecifier("/Audio/Effects/burning.ogg"); - [DataField("cremateFinishSound")] private SoundSpecifier _cremateFinishSound = new SoundPathSpecifier("/Audio/Machines/ding.ogg"); - - [ViewVariables] - public bool Cooking { get; private set; } - - [ViewVariables(VVAccess.ReadWrite)] - private int _burnMilis = 5000; - - private CancellationTokenSource? _cremateCancelToken; - - public override bool CanOpen(EntityUid user, bool silent = false) - { - if (Cooking) - { - if (!silent) - Owner.PopupMessage(user, Loc.GetString("crematorium-entity-storage-component-is-cooking-safety-message")); - return false; - } - return base.CanOpen(user, silent); - } - - public void TryCremate() - { - if (Cooking) return; - if (Open) return; - - SoundSystem.Play(_cremateStartSound.GetSound(), Filter.Pvs(Owner), Owner); - - Cremate(); - } - - public void Cremate() - { - if (Open) - CloseStorage(); - - if(_entities.TryGetComponent(Owner, out AppearanceComponent? appearanceComponent)) - appearanceComponent.SetData(CrematoriumVisuals.Burning, true); - Cooking = true; - - SoundSystem.Play(_crematingSound.GetSound(), Filter.Pvs(Owner), Owner); - - _cremateCancelToken?.Cancel(); - - _cremateCancelToken = new CancellationTokenSource(); - Owner.SpawnTimer(_burnMilis, () => - { - if (_entities.Deleted(Owner)) - return; - if(_entities.TryGetComponent(Owner, out appearanceComponent)) - appearanceComponent.SetData(CrematoriumVisuals.Burning, false); - Cooking = false; - - if (Contents.ContainedEntities.Count > 0) - { - for (var i = Contents.ContainedEntities.Count - 1; i >= 0; i--) - { - var item = Contents.ContainedEntities[i]; - Contents.Remove(item); - _entities.DeleteEntity(item); - } - - var ash = _entities.SpawnEntity("Ash", _entities.GetComponent(Owner).Coordinates); - Contents.Insert(ash); - } - - TryOpenStorage(Owner); - - SoundSystem.Play(_cremateFinishSound.GetSound(), Filter.Pvs(Owner), Owner); - - }, _cremateCancelToken.Token); - } - } -} diff --git a/Content.Server/Morgue/Components/EntityStorageLayingDownOverrideComponent.cs b/Content.Server/Morgue/Components/EntityStorageLayingDownOverrideComponent.cs new file mode 100644 index 0000000000..b94635f6f1 --- /dev/null +++ b/Content.Server/Morgue/Components/EntityStorageLayingDownOverrideComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.Morgue.Components; + +[RegisterComponent] +public sealed class EntityStorageLayingDownOverrideComponent : Component +{ + +} diff --git a/Content.Server/Morgue/Components/MorgueComponent.cs b/Content.Server/Morgue/Components/MorgueComponent.cs new file mode 100644 index 0000000000..0327aae241 --- /dev/null +++ b/Content.Server/Morgue/Components/MorgueComponent.cs @@ -0,0 +1,26 @@ +using Content.Shared.Sound; + +namespace Content.Server.Morgue.Components; + +[RegisterComponent] +public sealed class MorgueComponent : Component +{ + /// + /// Whether or not the morgue beeps if a living player is inside. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("doSoulBeep")] + public bool DoSoulBeep = true; + + [ViewVariables] + public float AccumulatedFrameTime = 0f; + + /// + /// The amount of time between each beep. + /// + [ViewVariables] + public float BeepTime = 10f; + + [DataField("occupantHasSoulAlarmSound")] + public SoundSpecifier OccupantHasSoulAlarmSound = new SoundPathSpecifier("/Audio/Weapons/Guns/EmptyAlarm/smg_empty_alarm.ogg"); +} diff --git a/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs b/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs deleted file mode 100644 index a387303d3f..0000000000 --- a/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs +++ /dev/null @@ -1,173 +0,0 @@ -using Content.Server.Storage.Components; -using Content.Shared.Body.Components; -using Content.Shared.Directions; -using Content.Shared.Interaction; -using Content.Shared.Morgue; -using Content.Shared.Physics; -using Content.Shared.Popups; -using Content.Shared.Sound; -using Content.Shared.Standing; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Containers; -using Robust.Shared.Map; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Morgue.Components -{ - [RegisterComponent] - [ComponentReference(typeof(EntityStorageComponent))] - [ComponentReference(typeof(IActivate))] - [ComponentReference(typeof(IStorageComponent))] - [Virtual] - public class MorgueEntityStorageComponent : EntityStorageComponent - { - [Dependency] private readonly IEntityManager _entMan = default!; - - private const CollisionGroup TrayCanOpenMask = CollisionGroup.Impassable | CollisionGroup.MidImpassable; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("trayPrototype", customTypeSerializer:typeof(PrototypeIdSerializer))] - private string? _trayPrototypeId; - - [ViewVariables] - private EntityUid? _tray; - - [ViewVariables] - public ContainerSlot? TrayContainer { get; private set; } - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("doSoulBeep")] - public bool DoSoulBeep = true; - - [DataField("occupantHasSoulAlarmSound")] - private SoundSpecifier _occupantHasSoulAlarmSound = new SoundPathSpecifier("/Audio/Weapons/Guns/EmptyAlarm/smg_empty_alarm.ogg"); - - protected override void Initialize() - { - base.Initialize(); - if(_entMan.TryGetComponent(Owner, out var appearance)) - appearance.SetData(MorgueVisuals.Open, false); - TrayContainer = Owner.EnsureContainer("morgue_tray", out _); - } - - public override Vector2 ContentsDumpPosition() - { - if (_tray != null) - return _entMan.GetComponent(_tray.Value).WorldPosition; - return base.ContentsDumpPosition(); - } - - protected override bool AddToContents(EntityUid entity) - { - if (_entMan.HasComponent(entity) && !EntitySystem.Get().IsDown(entity)) - return false; - return base.AddToContents(entity); - } - - public override bool CanOpen(EntityUid user, bool silent = false) - { - if (!EntitySystem.Get().InRangeUnobstructed(Owner, - _entMan.GetComponent(Owner).Coordinates.Offset(_entMan.GetComponent(Owner).LocalRotation.GetCardinalDir()), - collisionMask: TrayCanOpenMask - )) - { - if (!silent) - Owner.PopupMessage(user, Loc.GetString("morgue-entity-storage-component-cannot-open-no-space")); - return false; - } - - return base.CanOpen(user, silent); - } - - protected override void OpenStorage() - { - if (_entMan.TryGetComponent(Owner, out var appearance)) - { - appearance.SetData(MorgueVisuals.Open, true); - appearance.SetData(MorgueVisuals.HasContents, false); - appearance.SetData(MorgueVisuals.HasMob, false); - appearance.SetData(MorgueVisuals.HasSoul, false); - } - - if (_tray == null) - { - _tray = _entMan.SpawnEntity(_trayPrototypeId, _entMan.GetComponent(Owner).Coordinates); - var trayComp = _tray.Value.EnsureComponent(); - trayComp.Morgue = Owner; - } - else - { - TrayContainer?.Remove(_tray.Value); - } - - _entMan.GetComponent(_tray.Value).Coordinates = new EntityCoordinates(Owner, 0, -1); - - base.OpenStorage(); - } - - private void CheckContents() - { - var count = 0; - var hasMob = false; - var hasSoul = false; - foreach (var entity in Contents.ContainedEntities) - { - count++; - if (!hasMob && _entMan.HasComponent(entity)) - hasMob = true; - if (!hasSoul && _entMan.TryGetComponent(entity, out var actor) && actor.PlayerSession != null) - hasSoul = true; - } - - if (_entMan.TryGetComponent(Owner, out var appearance)) - { - appearance.SetData(MorgueVisuals.HasContents, count > 0); - appearance.SetData(MorgueVisuals.HasMob, hasMob); - appearance.SetData(MorgueVisuals.HasSoul, hasSoul); - } - } - - protected override void CloseStorage() - { - base.CloseStorage(); - - if (_entMan.TryGetComponent(Owner, out var appearance)) - appearance.SetData(MorgueVisuals.Open, false); - CheckContents(); - - if (_tray != null) - { - TrayContainer?.Insert(_tray.Value); - } - } - - protected override IEnumerable DetermineCollidingEntities() - { - if (_tray == null) - { - yield break; - } - - var entityLookup = EntitySystem.Get(); - foreach (var entity in entityLookup.GetEntitiesIntersecting(_tray.Value, flags: LookupFlags.None)) - { - yield return entity; - } - } - - //Called every 10 seconds - public void Update() - { - CheckContents(); - - if (DoSoulBeep && _entMan.TryGetComponent(Owner, out var appearance) && - appearance.TryGetData(MorgueVisuals.HasSoul, out bool hasSoul) && hasSoul) - { - SoundSystem.Play(_occupantHasSoulAlarmSound.GetSound(), Filter.Pvs(Owner), Owner); - } - } - } -} diff --git a/Content.Server/Morgue/Components/MorgueTrayComponent.cs b/Content.Server/Morgue/Components/MorgueTrayComponent.cs deleted file mode 100644 index 8f4fd84e62..0000000000 --- a/Content.Server/Morgue/Components/MorgueTrayComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Shared.Interaction; - -namespace Content.Server.Morgue.Components -{ - [RegisterComponent] - [ComponentReference(typeof(IActivate))] - public sealed class MorgueTrayComponent : Component, IActivate - { - [ViewVariables] - public EntityUid Morgue { get; set; } - - void IActivate.Activate(ActivateEventArgs eventArgs) - { - var entMan = IoCManager.Resolve(); - - if (Morgue != default && !entMan.Deleted(Morgue) && entMan.TryGetComponent(Morgue, out var comp)) - { - comp.Activate(new ActivateEventArgs(eventArgs.User, Morgue)); - } - } - } -} diff --git a/Content.Server/Morgue/CrematoriumSystem.cs b/Content.Server/Morgue/CrematoriumSystem.cs new file mode 100644 index 0000000000..e10f620843 --- /dev/null +++ b/Content.Server/Morgue/CrematoriumSystem.cs @@ -0,0 +1,162 @@ +using Content.Server.Morgue.Components; +using Content.Shared.Morgue; +using Robust.Server.GameObjects; +using Robust.Shared.Player; +using Robust.Shared.Audio; +using Content.Server.Storage.Components; +using System.Threading; +using Content.Shared.Verbs; +using Content.Shared.Database; +using Content.Shared.Interaction.Events; +using Content.Server.Players; +using Content.Server.GameTicking; +using Content.Shared.Popups; +using Content.Server.Storage.EntitySystems; +using Content.Shared.Examine; +using Content.Shared.Standing; +using Content.Shared.Storage; + +namespace Content.Server.Morgue; + +public sealed class CrematoriumSystem : EntitySystem +{ + [Dependency] private readonly GameTicker _ticker = default!; + [Dependency] private readonly EntityStorageSystem _entityStorage = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnAttemptOpen); + SubscribeLocalEvent>(AddCremateVerb); + SubscribeLocalEvent(OnSuicide); + } + + private void OnExamine(EntityUid uid, CrematoriumComponent component, ExaminedEvent args) + { + if (!TryComp(uid, out var appearance)) + return; + + if (appearance.TryGetData(CrematoriumVisuals.Burning, out bool isBurning) && isBurning) + args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-is-burning", ("owner", uid))); + if (appearance.TryGetData(StorageVisuals.HasContents, out bool hasContents) && hasContents) + args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-has-contents")); + else + args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-empty")); + } + + private void OnAttemptOpen(EntityUid uid, CrematoriumComponent component, StorageOpenAttemptEvent args) + { + if (component.Cooking) + args.Cancel(); + } + + private void AddCremateVerb(EntityUid uid, CrematoriumComponent component, GetVerbsEvent args) + { + if (!TryComp(uid, out var storage)) + return; + + if (!args.CanAccess || !args.CanInteract || args.Hands == null || component.Cooking || storage.Open) + return; + + AlternativeVerb verb = new() + { + Text = Loc.GetString("cremate-verb-get-data-text"), + // TODO VERB ICON add flame/burn symbol? + Act = () => TryCremate(uid, component, storage), + Impact = LogImpact.Medium // could be a body? or evidence? I dunno. + }; + args.Verbs.Add(verb); + } + + public void Cremate(EntityUid uid, CrematoriumComponent? component = null, EntityStorageComponent? storage = null) + { + if (!Resolve(uid, ref component, ref storage)) + return; + + if (TryComp(uid, out var app)) + app.SetData(CrematoriumVisuals.Burning, true); + component.Cooking = true; + + SoundSystem.Play(component.CrematingSound.GetSound(), Filter.Pvs(uid), uid); + + component.CremateCancelToken?.Cancel(); + component.CremateCancelToken = new CancellationTokenSource(); + uid.SpawnTimer(component.BurnMilis, () => + { + if (Deleted(uid)) + return; + if (TryComp(uid, out var app)) + app.SetData(CrematoriumVisuals.Burning, false); + component.Cooking = false; + + if (storage.Contents.ContainedEntities.Count > 0) + { + for (var i = storage.Contents.ContainedEntities.Count - 1; i >= 0; i--) + { + var item = storage.Contents.ContainedEntities[i]; + storage.Contents.Remove(item); + EntityManager.DeleteEntity(item); + } + + var ash = Spawn("Ash", Transform(uid).Coordinates); + storage.Contents.Insert(ash); + } + + _entityStorage.OpenStorage(uid, storage); + + SoundSystem.Play(component.CremateFinishSound.GetSound(), Filter.Pvs(uid), uid); + + }, component.CremateCancelToken.Token); + } + + public void TryCremate(EntityUid uid, CrematoriumComponent component, EntityStorageComponent? storage = null) + { + if (!Resolve(uid, ref storage)) + return; + + if (component.Cooking || storage.Open || storage.Contents.ContainedEntities.Count < 1) + return; + + SoundSystem.Play(component.CremateStartSound.GetSound(), Filter.Pvs(uid), uid); + + Cremate(uid, component, storage); + } + + private void OnSuicide(EntityUid uid, CrematoriumComponent component, SuicideEvent args) + { + if (args.Handled) + return; + args.SetHandled(SuicideKind.Heat); + + var victim = args.Victim; + if (TryComp(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is { } mind) + { + _ticker.OnGhostAttempt(mind, false); + + if (mind.OwnedEntity is { Valid: true } entity) + { + _popup.PopupEntity(Loc.GetString("crematorium-entity-storage-component-suicide-message"), entity, Filter.Pvs(entity)); + } + } + + _popup.PopupEntity(Loc.GetString("crematorium-entity-storage-component-suicide-message-others", ("victim", victim)), + victim, Filter.PvsExcept(victim), PopupType.LargeCaution); + + if (_entityStorage.CanInsert(uid)) + { + _entityStorage.CloseStorage(uid); + _standing.Down(victim, false); + _entityStorage.Insert(victim, uid); + } + else + { + EntityManager.DeleteEntity(victim); + } + _entityStorage.CloseStorage(uid); + Cremate(uid, component); + } +} diff --git a/Content.Server/Morgue/EntityStorageLayingDownOverrideSystem.cs b/Content.Server/Morgue/EntityStorageLayingDownOverrideSystem.cs new file mode 100644 index 0000000000..d200952522 --- /dev/null +++ b/Content.Server/Morgue/EntityStorageLayingDownOverrideSystem.cs @@ -0,0 +1,25 @@ +using Content.Server.Morgue.Components; +using Content.Shared.Standing; +using Content.Server.Storage.Components; +using Content.Shared.Body.Components; + +namespace Content.Server.Morgue; + +public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem +{ + [Dependency] private readonly StandingStateSystem _standing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeClose); + } + + private void OnBeforeClose(EntityUid uid, EntityStorageLayingDownOverrideComponent component, StorageBeforeCloseEvent args) + { + foreach (var ent in args.Contents) + if (HasComp(ent) && !_standing.IsDown(ent)) + args.Contents.Remove(ent); + } +} diff --git a/Content.Server/Morgue/MorgueSystem.cs b/Content.Server/Morgue/MorgueSystem.cs index 0a59fafa8e..79eb0762eb 100644 --- a/Content.Server/Morgue/MorgueSystem.cs +++ b/Content.Server/Morgue/MorgueSystem.cs @@ -1,145 +1,93 @@ using Content.Server.Morgue.Components; using Content.Shared.Morgue; using Content.Shared.Examine; -using Content.Shared.Database; -using Content.Shared.Verbs; -using JetBrains.Annotations; -using Content.Shared.Interaction.Events; using Robust.Server.GameObjects; -using Content.Server.Players; -using Content.Server.GameTicking; using Content.Server.Popups; -using Content.Shared.Popups; -using Content.Shared.Standing; using Robust.Shared.Player; +using Robust.Shared.Audio; +using Content.Server.Storage.Components; +using Content.Shared.Body.Components; +using Content.Shared.Storage; -namespace Content.Server.Morgue +namespace Content.Server.Morgue; + +public sealed partial class MorgueSystem : EntitySystem { - [UsedImplicitly] - public sealed class MorgueSystem : EntitySystem + public override void Initialize() { - [Dependency] private readonly GameTicker _ticker = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly StandingStateSystem _stando = default!; + base.Initialize(); - private float _accumulatedFrameTime; + SubscribeLocalEvent(OnExamine); + } - public override void Initialize() + /// + /// Handles the examination text for looking at a morgue. + /// + private void OnExamine(EntityUid uid, MorgueComponent component, ExaminedEvent args) + { + if (!TryComp(uid, out var appearance)) + return; + + if (!args.IsInDetailsRange) + return; + + if (appearance.TryGetData(MorgueVisuals.HasSoul, out bool hasSoul) && hasSoul) + args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-body-has-soul")); + else if (appearance.TryGetData(MorgueVisuals.HasMob, out bool hasMob) && hasMob) + args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-body-has-no-soul")); + else if (appearance.TryGetData(StorageVisuals.HasContents, out bool hasContents) && hasContents) + args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-has-contents")); + else + args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-empty")); + } + + /// + /// Updates data periodically in case something died/got deleted in the morgue. + /// + private void CheckContents(EntityUid uid, MorgueComponent? morgue = null, EntityStorageComponent? storage = null) + { + if (!Resolve(uid, ref morgue, ref storage)) + return; + + var hasMob = false; + var hasSoul = false; + + foreach (var ent in storage.Contents.ContainedEntities) { - base.Initialize(); - - SubscribeLocalEvent>(AddCremateVerb); - SubscribeLocalEvent(OnCrematoriumExamined); - SubscribeLocalEvent(OnSuicide); - SubscribeLocalEvent(OnMorgueExamined); + if (!hasMob && HasComp(ent)) + hasMob = true; + if (!hasSoul && TryComp(ent, out var actor) && actor.PlayerSession != null) + hasSoul = true; } - private void OnSuicide(EntityUid uid, CrematoriumEntityStorageComponent component, SuicideEvent args) + if (TryComp(uid, out var app)) { - if (args.Handled) return; - args.SetHandled(SuicideKind.Heat); - var victim = args.Victim; - if (TryComp(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is { } mind) - { - _ticker.OnGhostAttempt(mind, false); - - if (mind.OwnedEntity is { Valid: true } entity) - { - _popup.PopupEntity(Loc.GetString("crematorium-entity-storage-component-suicide-message"), entity, - Filter.Pvs(entity, entityManager: EntityManager), PopupType.MediumCaution); - } - } - - _popup.PopupEntity( - Loc.GetString("crematorium-entity-storage-component-suicide-message-others", ("victim", victim)), - victim, - Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim)); - - if (component.CanInsert(victim)) - { - component.Insert(victim); - _stando.Down(victim, false); - } - else - { - - EntityManager.DeleteEntity(victim); - } - - component.Cremate(); + app.SetData(MorgueVisuals.HasMob, hasMob); + app.SetData(MorgueVisuals.HasSoul, hasSoul); } + } - private void AddCremateVerb(EntityUid uid, CrematoriumEntityStorageComponent component, GetVerbsEvent args) + /// + /// Handles the periodic beeping that morgues do when a live body is inside. + /// + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var comp in EntityQuery()) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null || component.Cooking || component.Open ) - return; + comp.AccumulatedFrameTime += frameTime; - AlternativeVerb verb = new(); - verb.Text = Loc.GetString("cremate-verb-get-data-text"); - // TODO VERB ICON add flame/burn symbol? - verb.Act = () => component.TryCremate(); - verb.Impact = LogImpact.Medium; // could be a body? or evidence? I dunno. - args.Verbs.Add(verb); - } + CheckContents(comp.Owner, comp); - private void OnCrematoriumExamined(EntityUid uid, CrematoriumEntityStorageComponent component, ExaminedEvent args) - { - if (!TryComp(uid, out var appearance)) - return; + if (comp.AccumulatedFrameTime < comp.BeepTime) + continue; + comp.AccumulatedFrameTime -= comp.BeepTime; - if (args.IsInDetailsRange) + if (comp.DoSoulBeep && TryComp(comp.Owner, out var appearance) && + appearance.TryGetData(MorgueVisuals.HasSoul, out bool hasSoul) && hasSoul) { - if (appearance.TryGetData(CrematoriumVisuals.Burning, out bool isBurning) && isBurning) - { - args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-is-burning", ("owner", uid))); - } - - if (appearance.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) - { - args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-has-contents")); - } - else - { - args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-empty")); - } - } - } - - private void OnMorgueExamined(EntityUid uid, MorgueEntityStorageComponent component, ExaminedEvent args) - { - if (!TryComp(uid, out var appearance)) return; - - if (args.IsInDetailsRange) - { - if (appearance.TryGetData(MorgueVisuals.HasSoul, out bool hasSoul) && hasSoul) - { - args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-body-has-soul")); - } - else if (appearance.TryGetData(MorgueVisuals.HasMob, out bool hasMob) && hasMob) - { - args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-body-has-no-soul")); - } - else if (appearance.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) - { - args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-has-contents")); - } - else - { - args.PushMarkup(Loc.GetString("morgue-entity-storage-component-on-examine-details-empty")); - } - } - } - public override void Update(float frameTime) - { - _accumulatedFrameTime += frameTime; - - if (_accumulatedFrameTime >= 10) - { - foreach (var morgue in EntityManager.EntityQuery()) - { - morgue.Update(); - } - _accumulatedFrameTime -= 10; + SoundSystem.Play(comp.OccupantHasSoulAlarmSound.GetSound(), Filter.Pvs(comp.Owner), comp.Owner); } } } diff --git a/Content.Server/Resist/ResistLockerSystem.cs b/Content.Server/Resist/ResistLockerSystem.cs index 303eada916..ebbf30b554 100644 --- a/Content.Server/Resist/ResistLockerSystem.cs +++ b/Content.Server/Resist/ResistLockerSystem.cs @@ -6,6 +6,7 @@ using Robust.Shared.Player; using Robust.Shared.Containers; using Content.Server.Popups; using Content.Shared.Movement.Events; +using Content.Server.Storage.EntitySystems; using Content.Shared.Popups; namespace Content.Server.Resist; @@ -15,6 +16,7 @@ public sealed class ResistLockerSystem : EntitySystem [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly LockSystem _lockSystem = default!; + [Dependency] private readonly EntityStorageSystem _entityStorage = default!; public override void Initialize() { @@ -74,7 +76,7 @@ public sealed class ResistLockerSystem : EntitySystem _lockSystem.Unlock(uid, ev.User, lockComponent); component.CancelToken = null; - storageComponent.TryOpenStorage(ev.User); + _entityStorage.TryOpenStorage(ev.User, storageComponent.Owner); } } diff --git a/Content.Server/Storage/Components/ArtifactStorageComponent.cs b/Content.Server/Storage/Components/ArtifactStorageComponent.cs index 13684aee68..4d1153cf90 100644 --- a/Content.Server/Storage/Components/ArtifactStorageComponent.cs +++ b/Content.Server/Storage/Components/ArtifactStorageComponent.cs @@ -1,14 +1,7 @@ -using Content.Server.Xenoarchaeology.XenoArtifacts; - namespace Content.Server.Storage.Components; [RegisterComponent] -public sealed class ArtifactStorageComponent : EntityStorageComponent +public sealed class ArtifactStorageComponent : Component { - [Dependency] private readonly IEntityManager _entMan = default!; - public override bool CanFit(EntityUid entity) - { - return _entMan.HasComponent(entity); - } } diff --git a/Content.Server/Storage/Components/CursedEntityStorageComponent.cs b/Content.Server/Storage/Components/CursedEntityStorageComponent.cs index 87f53c320d..d268b22743 100644 --- a/Content.Server/Storage/Components/CursedEntityStorageComponent.cs +++ b/Content.Server/Storage/Components/CursedEntityStorageComponent.cs @@ -1,54 +1,12 @@ using Content.Shared.Audio; using Content.Shared.Interaction; using Content.Shared.Sound; -using Robust.Shared.Audio; -using Robust.Shared.Player; -using Robust.Shared.Random; -using System.Linq; -namespace Content.Server.Storage.Components +namespace Content.Server.Storage.Components; + +[RegisterComponent] +public sealed class CursedEntityStorageComponent : Component { - [ComponentReference(typeof(EntityStorageComponent))] - [ComponentReference(typeof(IActivate))] - [ComponentReference(typeof(IStorageComponent))] - [RegisterComponent] - public sealed class CursedEntityStorageComponent : EntityStorageComponent - { - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - - [DataField("cursedSound")] private SoundSpecifier _cursedSound = new SoundPathSpecifier("/Audio/Effects/teleport_departure.ogg"); - [DataField("cursedLockerSound")] private SoundSpecifier _cursedLockerSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg"); - - protected override void CloseStorage() - { - base.CloseStorage(); - - // No contents, we do nothing - if (Contents.ContainedEntities.Count == 0) return; - - var lockers = _entMan.EntityQuery().Select(c => c.Owner).ToList(); - - if (lockers.Contains(Owner)) - lockers.Remove(Owner); - - if (lockers.Count == 0) return; - - var lockerEnt = _robustRandom.Pick(lockers); - - var locker = _entMan.GetComponent(lockerEnt); - - if (locker.Open) - locker.TryCloseStorage(Owner); - - foreach (var entity in Contents.ContainedEntities.ToArray()) - { - Contents.ForceRemove(entity); - locker.Insert(entity); - } - - SoundSystem.Play(_cursedSound.GetSound(), Filter.Pvs(Owner), Owner, AudioHelpers.WithVariation(0.125f)); - SoundSystem.Play(_cursedLockerSound.GetSound(), Filter.Pvs(lockerEnt), lockerEnt, AudioHelpers.WithVariation(0.125f)); - } - } + [DataField("cursedSound")] + public SoundSpecifier CursedSound = new SoundPathSpecifier("/Audio/Effects/teleport_departure.ogg"); } diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs index 71f7fb0ed3..93e4bd02fc 100644 --- a/Content.Server/Storage/Components/EntityStorageComponent.cs +++ b/Content.Server/Storage/Components/EntityStorageComponent.cs @@ -1,389 +1,104 @@ -using System.Linq; -using Content.Server.Buckle.Components; -using Content.Server.Construction; -using Content.Server.Construction.Completions; -using Content.Server.Construction.Components; -using Content.Server.Ghost.Components; -using Content.Server.Storage.EntitySystems; -using Content.Shared.Body.Components; -using Content.Shared.Foldable; -using Content.Shared.Interaction; -using Content.Shared.Item; using Content.Shared.Physics; -using Content.Shared.Placeable; -using Content.Shared.Popups; using Content.Shared.Sound; -using Content.Shared.Storage; -using Robust.Shared.Audio; using Robust.Shared.Containers; -using Robust.Shared.Physics; -using Robust.Shared.Player; -namespace Content.Server.Storage.Components +namespace Content.Server.Storage.Components; + +[RegisterComponent] +public sealed class EntityStorageComponent : Component { - [RegisterComponent] - [Virtual] - [ComponentReference(typeof(IActivate))] - [ComponentReference(typeof(IStorageComponent))] - public class EntityStorageComponent : Component, IActivate, IStorageComponent + public readonly float MaxSize = 1.0f; // maximum width or height of an entity allowed inside the storage. + + public static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5); + public TimeSpan LastInternalOpenAttempt; + + /// + /// Collision masks that get removed when the storage gets opened. + /// + public readonly int MasksToRemove = (int) ( + CollisionGroup.MidImpassable | + CollisionGroup.HighImpassable | + CollisionGroup.LowImpassable); + + /// + /// Collision masks that were removed from ANY layer when the storage was opened; + /// + [DataField("removedMasks")] + public int RemovedMasks; + + [ViewVariables] + [DataField("Capacity")] + public int StorageCapacityMax = 30; + + [ViewVariables] + [DataField("IsCollidableWhenOpen")] + public bool IsCollidableWhenOpen; + + //The offset for where items are emptied/vacuumed for the EntityStorage. + [DataField("enteringOffset")] + public Vector2 EnteringOffset = new(0, 0); + + //The collision groups checked, so that items are depositied or grabbed from inside walls. + [DataField("enteringOffsetCollisionFlags")] + public readonly CollisionGroup EnteringOffsetCollisionFlags = CollisionGroup.Impassable | CollisionGroup.MidImpassable; + + [ViewVariables] + [DataField("EnteringRange")] + public float EnteringRange = -0.18f; + + [DataField("showContents")] + public bool ShowContents; + + [DataField("occludesLight")] + public bool OccludesLight = true; + + [DataField("deleteContentsOnDestruction")] + public bool DeleteContentsOnDestruction = false; + + [DataField("open")] + public bool Open; + + [DataField("closeSound")] + public SoundSpecifier CloseSound = new SoundPathSpecifier("/Audio/Effects/closetclose.ogg"); + + [DataField("openSound")] + public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/closetopen.ogg"); + + [ViewVariables] + public Container Contents = default!; + + [ViewVariables(VVAccess.ReadWrite)] + public bool IsWeldedShut; +} + +public sealed class InsertIntoEntityStorageAttemptEvent : CancellableEntityEventArgs { } +public sealed class StoreMobInItemContainerAttemptEvent : CancellableEntityEventArgs +{ + public bool Handled = false; +} +public sealed class StorageOpenAttemptEvent : CancellableEntityEventArgs +{ + public bool Silent = false; + + public StorageOpenAttemptEvent (bool silent = false) { - [Dependency] private readonly IEntityManager _entMan = default!; - - private const float MaxSize = 1.0f; // maximum width or height of an entity allowed inside the storage. - - public static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5); - public TimeSpan LastInternalOpenAttempt; - - /// - /// Collision masks that get removed when the storage gets opened. - /// - private const int MasksToRemove = (int) ( - CollisionGroup.MidImpassable | - CollisionGroup.HighImpassable | - CollisionGroup.LowImpassable); - - /// - /// Collision masks that were removed from ANY layer when the storage was opened; - /// - [DataField("removedMasks")] public int RemovedMasks; - - [ViewVariables] - [DataField("Capacity")] - private int _storageCapacityMax = 30; - - [ViewVariables] - [DataField("IsCollidableWhenOpen")] - private bool _isCollidableWhenOpen; - - [ViewVariables] - [DataField("EnteringRange")] - private float _enteringRange = -0.18f; - - [DataField("showContents")] - private bool _showContents; - - [DataField("occludesLight")] - private bool _occludesLight = true; - - [DataField("open")] - public bool Open; - - [DataField("closeSound")] - private SoundSpecifier _closeSound = new SoundPathSpecifier("/Audio/Effects/closetclose.ogg"); - - [DataField("openSound")] - private SoundSpecifier _openSound = new SoundPathSpecifier("/Audio/Effects/closetopen.ogg"); - - [ViewVariables] - public Container Contents = default!; - - /// - /// Determines if the container contents should be drawn when the container is closed. - /// - [ViewVariables(VVAccess.ReadWrite)] - public bool ShowContents - { - get => _showContents; - set - { - _showContents = value; - Contents.ShowContents = _showContents; - } - } - - [ViewVariables(VVAccess.ReadWrite)] - public bool OccludesLight - { - get => _occludesLight; - set - { - _occludesLight = value; - Contents.OccludesLight = _occludesLight; - } - } - - [ViewVariables(VVAccess.ReadWrite)] - public bool IsWeldedShut; - - [ViewVariables(VVAccess.ReadWrite)] - public float EnteringRange - { - get => _enteringRange; - set => _enteringRange = value; - } - - /// - protected override void Initialize() - { - base.Initialize(); - Contents = Owner.EnsureContainer(EntityStorageSystem.ContainerName); - Contents.ShowContents = _showContents; - Contents.OccludesLight = _occludesLight; - - if(_entMan.TryGetComponent(Owner, out ConstructionComponent? construction)) - EntitySystem.Get().AddContainer(Owner, nameof(EntityStorageComponent), construction); - - if (_entMan.TryGetComponent(Owner, out var surface)) - { - EntitySystem.Get().SetPlaceable(Owner, Open, surface); - } - } - - public virtual void Activate(ActivateEventArgs eventArgs) - { - ToggleOpen(eventArgs.User); - } - - public virtual bool CanOpen(EntityUid user, bool silent = false) - { - if (IsWeldedShut) - { - if (!silent && !Contents.Contains(user)) - Owner.PopupMessage(user, Loc.GetString("entity-storage-component-welded-shut-message")); - - return false; - } - - if (_entMan.TryGetComponent(Owner, out var @lock) && @lock.Locked) - { - if (!silent) Owner.PopupMessage(user, Loc.GetString("entity-storage-component-locked-message")); - return false; - } - - var @event = new StorageOpenAttemptEvent(); - IoCManager.Resolve().EventBus.RaiseLocalEvent(Owner, @event, true); - - return !@event.Cancelled; - } - - public virtual bool CanClose(EntityUid user, bool silent = false) - { - var @event = new StorageCloseAttemptEvent(); - IoCManager.Resolve().EventBus.RaiseLocalEvent(Owner, @event, true); - - return !@event.Cancelled; - } - - public void ToggleOpen(EntityUid user) - { - if (Open) - { - TryCloseStorage(user); - } - else - { - TryOpenStorage(user); - } - } - - protected virtual void CloseStorage() - { - Open = false; - - var count = 0; - foreach (var entity in DetermineCollidingEntities()) - { - // prevents taking items out of inventories, out of containers, and orphaning child entities - if (entity.IsInContainer()) - continue; - - if (!CanFit(entity)) - continue; - - // finally, AddToContents - if (!AddToContents(entity)) - continue; - - count++; - if (count >= _storageCapacityMax) - { - break; - } - } - - ModifyComponents(); - SoundSystem.Play(_closeSound.GetSound(), Filter.Pvs(Owner), Owner); - LastInternalOpenAttempt = default; - } - - public virtual bool CanFit(EntityUid entity) - { - // conditions are complicated because of pizzabox-related issues, so follow this guide - // 0. Accomplish your goals at all costs. - // 1. AddToContents can block anything - // 2. maximum item count can block anything - // 3. ghosts can NEVER be eaten - // 4. items can always be eaten unless a previous law prevents it - // 5. if this is NOT AN ITEM, then mobs can always be eaten unless unless a previous law prevents it - // 6. if this is an item, then mobs must only be eaten if some other component prevents pick-up interactions while a mob is inside (e.g. foldable) - var attemptEvent = new InsertIntoEntityStorageAttemptEvent(); - _entMan.EventBus.RaiseLocalEvent(entity, attemptEvent); - if (attemptEvent.Cancelled) - return false; - - // checks - // TODO: Make the others sub to it. - var targetIsItem = _entMan.HasComponent(entity); - var targetIsMob = _entMan.HasComponent(entity); - var storageIsItem = _entMan.HasComponent(Owner); - - var allowedToEat = targetIsItem; - - // BEFORE REPLACING THIS WITH, I.E. A PROPERTY: - // Make absolutely 100% sure you have worked out how to stop people ending up in backpacks. - // Seriously, it is insanely hacky and weird to get someone out of a backpack once they end up in there. - // And to be clear, they should NOT be in there. - // For the record, what you need to do is empty the backpack onto a PlacableSurface (table, rack) - if (targetIsMob) - { - if (!storageIsItem) - allowedToEat = true; - else - { - var storeEv = new StoreThisAttemptEvent(); - _entMan.EventBus.RaiseLocalEvent(Owner, storeEv); - allowedToEat = !storeEv.Cancelled; - } - } - - return allowedToEat; - } - - protected virtual void OpenStorage() - { - Open = true; - EntitySystem.Get().EmptyContents(Owner, this); - ModifyComponents(); - SoundSystem.Play(_openSound.GetSound(), Filter.Pvs(Owner), Owner); - } - - private void ModifyComponents() - { - if (!_isCollidableWhenOpen && _entMan.TryGetComponent(Owner, out var manager) - && manager.Fixtures.Count > 0) - { - // currently only works for single-fixture entities. If they have more than one fixture, then - // RemovedMasks needs to be tracked separately for each fixture, using a fixture Id Dictionary. Also the - // fixture IDs probably cant be automatically generated without causing issues, unless there is some - // guarantee that they will get deserialized with the same auto-generated ID when saving+loading the map. - var fixture = manager.Fixtures.Values.First(); - - if (Open) - { - RemovedMasks = fixture.CollisionLayer & MasksToRemove; - fixture.CollisionLayer &= ~MasksToRemove; - } - else - { - fixture.CollisionLayer |= RemovedMasks; - RemovedMasks = 0; - } - } - - if (_entMan.TryGetComponent(Owner, out var surface)) - { - EntitySystem.Get().SetPlaceable(Owner, Open, surface); - } - - if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance)) - { - appearance.SetData(StorageVisuals.Open, Open); - } - } - - protected virtual bool AddToContents(EntityUid entity) - { - if (entity == Owner) return false; - if (_entMan.TryGetComponent(entity, out IPhysBody? entityPhysicsComponent)) - { - if (MaxSize < entityPhysicsComponent.GetWorldAABB().Size.X - || MaxSize < entityPhysicsComponent.GetWorldAABB().Size.Y) - { - return false; - } - } - - return Contents.CanInsert(entity) && Insert(entity); - } - - public virtual Vector2 ContentsDumpPosition() - { - return _entMan.GetComponent(Owner).WorldPosition; - } - - public virtual bool TryOpenStorage(EntityUid user) - { - if (!CanOpen(user)) return false; - OpenStorage(); - return true; - } - - public virtual bool TryCloseStorage(EntityUid user) - { - if (!CanClose(user)) return false; - CloseStorage(); - return true; - } - - /// - public bool Remove(EntityUid entity) - { - return Contents.CanRemove(entity); - } - - /// - public bool Insert(EntityUid entity) - { - // Trying to add while open just dumps it on the ground below us. - if (Open) - { - var entMan = _entMan; - entMan.GetComponent(entity).WorldPosition = entMan.GetComponent(Owner).WorldPosition; - return true; - } - - return Contents.Insert(entity); - } - - /// - public bool CanInsert(EntityUid entity) - { - if (Open) - { - return true; - } - - if (Contents.ContainedEntities.Count >= _storageCapacityMax) - { - return false; - } - - return Contents.CanInsert(entity); - } - - protected virtual IEnumerable DetermineCollidingEntities() - { - var entityLookup = EntitySystem.Get(); - return entityLookup.GetEntitiesInRange(Owner, _enteringRange, LookupFlags.Approximate); - } - } - - public sealed class InsertIntoEntityStorageAttemptEvent : CancellableEntityEventArgs - { - - } - - public sealed class StoreThisAttemptEvent : CancellableEntityEventArgs - { - - } - public sealed class StorageOpenAttemptEvent : CancellableEntityEventArgs - { - - } - - public sealed class StorageCloseAttemptEvent : CancellableEntityEventArgs - { - + Silent = silent; } } +public sealed class StorageAfterOpenEvent : EventArgs { } +public sealed class StorageCloseAttemptEvent : CancellableEntityEventArgs { } +public sealed class StorageBeforeCloseEvent : EventArgs +{ + public EntityUid Container; + + public HashSet Contents; + + public HashSet ContentsWhitelist = new(); + + public StorageBeforeCloseEvent(EntityUid container, HashSet contents) + { + Container = container; + Contents = contents; + } +} +public sealed class StorageAfterCloseEvent : EventArgs { } diff --git a/Content.Server/Storage/Components/IStorageComponent.cs b/Content.Server/Storage/Components/IStorageComponent.cs deleted file mode 100644 index 1689db3432..0000000000 --- a/Content.Server/Storage/Components/IStorageComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Content.Server.Storage.Components -{ - public interface IStorageComponent : IComponent - { - bool Remove(EntityUid entity); - bool Insert(EntityUid entity); - bool CanInsert(EntityUid entity); - } -} diff --git a/Content.Server/Storage/EntitySystems/ArtifactStorageSystem.cs b/Content.Server/Storage/EntitySystems/ArtifactStorageSystem.cs new file mode 100644 index 0000000000..972a296177 --- /dev/null +++ b/Content.Server/Storage/EntitySystems/ArtifactStorageSystem.cs @@ -0,0 +1,23 @@ +using Content.Server.Storage.Components; +using Content.Server.Xenoarchaeology.XenoArtifacts; + +namespace Content.Server.Storage.EntitySystems; + +public sealed class ArtifactStorageSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeClose); + } + + private void OnBeforeClose(EntityUid uid, ArtifactStorageComponent component, StorageBeforeCloseEvent args) + { + foreach (var ent in args.Contents) + { + if (HasComp(ent)) + args.ContentsWhitelist.Add(ent); + } + } +} diff --git a/Content.Server/Storage/EntitySystems/CursedEntityStorageSystem.cs b/Content.Server/Storage/EntitySystems/CursedEntityStorageSystem.cs new file mode 100644 index 0000000000..d9c5c14520 --- /dev/null +++ b/Content.Server/Storage/EntitySystems/CursedEntityStorageSystem.cs @@ -0,0 +1,47 @@ +using Content.Server.Storage.Components; +using Content.Shared.Audio; +using Content.Shared.Interaction; +using Robust.Server.Containers; +using Robust.Shared.Audio; +using Robust.Shared.Player; +using Robust.Shared.Random; +using System.Linq; + +namespace Content.Server.Storage.EntitySystems; + +public sealed class CursedEntityStorageSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EntityStorageSystem _entityStorage = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnClose); + } + + private void OnClose(EntityUid uid, CursedEntityStorageComponent component, StorageAfterCloseEvent args) + { + if (!TryComp(uid, out var storage)) + return; + + if (storage.Open || storage.Contents.ContainedEntities.Count <= 0) + return; + + var lockerQuery = EntityQuery().ToList(); + lockerQuery.Remove(storage); + + if (lockerQuery.Count == 0) + return; + + var lockerEnt = _random.Pick(lockerQuery).Owner; + + foreach (var entity in storage.Contents.ContainedEntities.ToArray()) + { + storage.Contents.Remove(entity); + _entityStorage.AddToContents(entity, lockerEnt); + } + SoundSystem.Play(component.CursedSound.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f, _random)); + } +} diff --git a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs index e72a22930c..d2ff9daa6c 100644 --- a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs +++ b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs @@ -1,8 +1,19 @@ -using System.Linq; +using System.Linq; +using Content.Server.Construction; +using Content.Server.Construction.Components; using Content.Server.Popups; using Content.Server.Storage.Components; using Content.Server.Tools.Systems; +using Content.Shared.Body.Components; using Content.Shared.Destructible; +using Content.Shared.Interaction; +using Content.Shared.Item; +using Content.Shared.Placeable; +using Content.Shared.Storage; +using Robust.Server.Containers; +using Robust.Shared.Audio; +using Robust.Shared.Containers; +using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Player; @@ -10,6 +21,11 @@ namespace Content.Server.Storage.EntitySystems; public sealed class EntityStorageSystem : EntitySystem { + [Dependency] private readonly ConstructionSystem _construction = default!; + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly PlaceableSurfaceSystem _placeableSurface = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; public const string ContainerName = "entity_storage"; @@ -17,11 +33,36 @@ public sealed class EntityStorageSystem : EntitySystem public override void Initialize() { base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnWeldableAttempt); SubscribeLocalEvent(OnWelded); SubscribeLocalEvent(OnDestroy); } + private void OnInit(EntityUid uid, EntityStorageComponent component, ComponentInit args) + { + component.Contents = _container.EnsureContainer(uid, ContainerName); + component.Contents.ShowContents = component.ShowContents; + component.Contents.OccludesLight = component.OccludesLight; + + if (TryComp(uid, out var construction)) + _construction.AddContainer(uid, nameof(EntityStorageComponent), construction); + + if (TryComp(uid, out var placeable)) + _placeableSurface.SetPlaceable(uid, component.Open, placeable); + } + + private void OnInteract(EntityUid uid, EntityStorageComponent component, ActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + ToggleOpen(args.User, uid, component); + } + private void OnWeldableAttempt(EntityUid uid, EntityStorageComponent component, WeldableAttemptEvent args) { if (component.Open) @@ -46,7 +87,23 @@ public sealed class EntityStorageSystem : EntitySystem private void OnDestroy(EntityUid uid, EntityStorageComponent component, DestructionEventArgs args) { component.Open = true; - EmptyContents(uid, component); + if (!component.DeleteContentsOnDestruction) + EmptyContents(uid, component); + } + + public void ToggleOpen(EntityUid user, EntityUid target, EntityStorageComponent? component = null) + { + if (!Resolve(target, ref component)) + return; + + if (component.Open) + { + TryCloseStorage(target); + } + else + { + TryOpenStorage(user, target); + } } public void EmptyContents(EntityUid uid, EntityStorageComponent? component = null) @@ -54,17 +111,252 @@ public sealed class EntityStorageSystem : EntitySystem if (!Resolve(uid, ref component)) return; + var uidXform = Transform(uid); var containedArr = component.Contents.ContainedEntities.ToArray(); foreach (var contained in containedArr) { if (component.Contents.Remove(contained)) { - Transform(contained).WorldPosition = component.ContentsDumpPosition(); - if (TryComp(contained, out IPhysBody? physics)) - { - physics.CanCollide = true; - } + Transform(contained).WorldPosition = + uidXform.WorldPosition + uidXform.WorldRotation.RotateVec(component.EnteringOffset); } } } + + public void OpenStorage(EntityUid uid, EntityStorageComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.Open = true; + EmptyContents(uid, component); + ModifyComponents(uid, component); + SoundSystem.Play(component.OpenSound.GetSound(), Filter.Pvs(component.Owner), component.Owner); + RaiseLocalEvent(uid, new StorageAfterOpenEvent()); + } + + public void CloseStorage(EntityUid uid, EntityStorageComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + component.Open = false; + + var targetCoordinates = new EntityCoordinates(uid, component.EnteringOffset); + + var ev = new StorageBeforeCloseEvent(uid, _lookup.GetEntitiesInRange(targetCoordinates, component.EnteringRange, LookupFlags.Approximate)); + RaiseLocalEvent(uid, ev, true); + + var count = 0; + foreach (var entity in ev.Contents) + { + if (!ev.ContentsWhitelist.Contains(entity)) + if (!CanFit(entity, uid)) + continue; + + if (!AddToContents(entity, uid, component)) + continue; + + count++; + if (count >= component.StorageCapacityMax) + break; + } + + ModifyComponents(uid, component); + SoundSystem.Play(component.CloseSound.GetSound(), Filter.Pvs(uid), uid); + component.LastInternalOpenAttempt = default; + RaiseLocalEvent(uid, new StorageAfterCloseEvent()); + } + + public bool Insert(EntityUid toInsert, EntityUid container, EntityStorageComponent? component = null) + { + if (!Resolve(container, ref component)) + return false; + + if (component.Open) + { + Transform(toInsert).WorldPosition = Transform(container).WorldPosition; + return true; + } + + return component.Contents.Insert(toInsert, EntityManager); + } + + public bool Remove(EntityUid toRemove, EntityUid container, EntityStorageComponent? component = null) + { + if (!Resolve(container, ref component)) + return false; + + return component.Contents.Remove(toRemove, EntityManager); + } + + public bool CanInsert(EntityUid container, EntityStorageComponent? component = null) + { + if (!Resolve(container, ref component)) + return false; + + if (component.Open) + return true; + + if (component.Contents.ContainedEntities.Count >= component.StorageCapacityMax) + return false; + + return true; + } + + public bool TryOpenStorage(EntityUid user, EntityUid target) + { + if (!CanOpen(user, target)) + return false; + + OpenStorage(target); + return true; + } + + public bool TryCloseStorage(EntityUid target) + { + if (!CanClose(target)) + { + return false; + } + + CloseStorage(target); + return true; + } + + public bool CanOpen(EntityUid user, EntityUid target, bool silent = false, EntityStorageComponent? component = null) + { + if (!Resolve(target, ref component)) + return false; + + if (component.IsWeldedShut) + { + if (!silent && !component.Contents.Contains(user)) + _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-welded-shut-message"), target, Filter.Pvs(target)); + + return false; + } + + //Checks to see if the opening position, if offset, is inside of a wall. + if (component.EnteringOffset != (0, 0)) //if the entering position is offset + { + var targetXform = Transform(target); + var newCoords = new EntityCoordinates(target, component.EnteringOffset); + if (!_interactionSystem.InRangeUnobstructed(target, newCoords, collisionMask: component.EnteringOffsetCollisionFlags)) + { + if (!silent) + _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-cannot-open-no-space"), target, Filter.Pvs(target)); + + return false; + } + } + + var ev = new StorageOpenAttemptEvent(silent); + RaiseLocalEvent(target, ev, true); + + return !ev.Cancelled; + } + + public bool CanClose(EntityUid target, bool silent = false) + { + var ev = new StorageCloseAttemptEvent(); + RaiseLocalEvent(target, ev, silent); + + return !ev.Cancelled; + } + + public bool AddToContents(EntityUid toAdd, EntityUid container, EntityStorageComponent? component = null) + { + if (!Resolve(container, ref component)) + return false; + + if (toAdd == container) + return false; + + if (TryComp(toAdd, out var phys)) + if (component.MaxSize < phys.GetWorldAABB().Size.X || component.MaxSize < phys.GetWorldAABB().Size.Y) + return false; + + return Insert(toAdd, container, component); + } + + public bool CanFit(EntityUid toInsert, EntityUid container) + { + // conditions are complicated because of pizzabox-related issues, so follow this guide + // 0. Accomplish your goals at all costs. + // 1. AddToContents can block anything + // 2. maximum item count can block anything + // 3. ghosts can NEVER be eaten + // 4. items can always be eaten unless a previous law prevents it + // 5. if this is NOT AN ITEM, then mobs can always be eaten unless a previous + // law prevents it + // 6. if this is an item, then mobs must only be eaten if some other component prevents + // pick-up interactions while a mob is inside (e.g. foldable) + var attemptEvent = new InsertIntoEntityStorageAttemptEvent(); + RaiseLocalEvent(toInsert, attemptEvent); + if (attemptEvent.Cancelled) + return false; + + // checks + // TODO: Make the others sub to it. + var targetIsItem = HasComp(toInsert); + var targetIsMob = HasComp(toInsert); + var storageIsItem = HasComp(container); + + var allowedToEat = targetIsItem; + + // BEFORE REPLACING THIS WITH, I.E. A PROPERTY: + // Make absolutely 100% sure you have worked out how to stop people ending up in backpacks. + // Seriously, it is insanely hacky and weird to get someone out of a backpack once they end up in there. + // And to be clear, they should NOT be in there. + // For the record, what you need to do is empty the backpack onto a PlacableSurface (table, rack) + if (targetIsMob) + { + if (!storageIsItem) + allowedToEat = true; + else + { + var storeEv = new StoreMobInItemContainerAttemptEvent(); + RaiseLocalEvent(container, storeEv); + allowedToEat = storeEv.Handled && !storeEv.Cancelled; + } + } + + return allowedToEat; + } + + public void ModifyComponents(EntityUid uid, EntityStorageComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (!component.IsCollidableWhenOpen && TryComp(uid, out var fixtures) && fixtures.Fixtures.Count > 0) + { + // currently only works for single-fixture entities. If they have more than one fixture, then + // RemovedMasks needs to be tracked separately for each fixture, using a fixture Id Dictionary. Also the + // fixture IDs probably cant be automatically generated without causing issues, unless there is some + // guarantee that they will get deserialized with the same auto-generated ID when saving+loading the map. + var fixture = fixtures.Fixtures.Values.First(); + + if (component.Open) + { + component.RemovedMasks = fixture.CollisionLayer & component.MasksToRemove; + fixture.CollisionLayer &= ~component.MasksToRemove; + } + else + { + fixture.CollisionLayer |= component.RemovedMasks; + component.RemovedMasks = 0; + } + } + + if (TryComp(uid, out var surface)) + _placeableSurface.SetPlaceable(uid, true, surface); + + if (TryComp(uid, out var appearance)) + { + appearance.SetData(StorageVisuals.Open, component.Open); + appearance.SetData(StorageVisuals.HasContents, component.Contents.ContainedEntities.Count() > 0); + } + + } } diff --git a/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs b/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs index 941cd5369c..77fb387b41 100644 --- a/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs +++ b/Content.Server/Storage/EntitySystems/StorageSystem.Fill.cs @@ -8,10 +8,12 @@ public sealed partial class StorageSystem private void OnStorageFillMapInit(EntityUid uid, StorageFillComponent component, MapInitEvent args) { if (component.Contents.Count == 0) return; - // ServerStorageComponent needs to rejoin IStorageComponent when other storage components are ECS'd - TryComp(uid, out var storage); + if (!EntityManager.EntitySysManager.TryGetEntitySystem(out var entityStorage)) return; + TryComp(uid, out var serverStorageComp); - if (storage == null && serverStorageComp == null) + TryComp(uid, out var entityStorageComp); + + if (entityStorageComp == null && serverStorageComp == null) { Logger.Error($"StorageFillComponent couldn't find any StorageComponent ({uid})"); return; @@ -25,7 +27,7 @@ public sealed partial class StorageSystem var ent = EntityManager.SpawnEntity(item, coordinates); // handle depending on storage component, again this should be unified after ECS - if (storage != null && storage.Insert(ent)) + if (entityStorageComp != null && entityStorage.Insert(ent, uid)) continue; if (serverStorageComp != null && Insert(uid, ent, serverStorageComp)) diff --git a/Content.Server/Storage/EntitySystems/StorageSystem.cs b/Content.Server/Storage/EntitySystems/StorageSystem.cs index 0db9ab9d4d..b3459f13f1 100644 --- a/Content.Server/Storage/EntitySystems/StorageSystem.cs +++ b/Content.Server/Storage/EntitySystems/StorageSystem.cs @@ -40,6 +40,7 @@ namespace Content.Server.Storage.EntitySystems [Dependency] private readonly ContainerSystem _containerSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; + [Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly InteractionSystem _interactionSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; @@ -88,12 +89,11 @@ namespace Content.Server.Storage.EntitySystems if (!EntityManager.HasComponent(args.Entity)) return; - if (_gameTiming.CurTime < - component.LastInternalOpenAttempt + EntityStorageComponent.InternalOpenAttemptDelay) + if (_gameTiming.CurTime < component.LastInternalOpenAttempt + EntityStorageComponent.InternalOpenAttemptDelay) return; component.LastInternalOpenAttempt = _gameTiming.CurTime; - component.TryOpenStorage(args.Entity); + _entityStorage.TryOpenStorage(args.Entity, component.Owner); } @@ -102,7 +102,7 @@ namespace Content.Server.Storage.EntitySystems if (!args.CanAccess || !args.CanInteract) return; - if (!component.CanOpen(args.User, silent: true)) + if (!_entityStorage.CanOpen(args.User, args.Target, silent: true, component)) return; InteractionVerb verb = new(); @@ -116,7 +116,7 @@ namespace Content.Server.Storage.EntitySystems verb.Text = Loc.GetString("verb-common-open"); verb.IconTexture = "/Textures/Interface/VerbIcons/open.svg.192dpi.png"; } - verb.Act = () => component.ToggleOpen(args.User); + verb.Act = () => _entityStorage.ToggleOpen(args.User, args.Target, component); args.Verbs.Add(verb); } diff --git a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs index 479e8ce956..fa704aef22 100644 --- a/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs +++ b/Content.Server/Traitor/Uplink/SurplusBundle/SurplusBundleSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Server.Storage.Components; +using Content.Server.Storage.EntitySystems; using Content.Shared.PDA; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -10,6 +11,7 @@ public sealed class SurplusBundleSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EntityStorageSystem _entityStorage = default!; private UplinkStoreListingPrototype[] _uplinks = default!; @@ -31,13 +33,12 @@ public sealed class SurplusBundleSystem : EntitySystem private void OnMapInit(EntityUid uid, SurplusBundleComponent component, MapInitEvent args) { - FillStorage(uid, component: component); + FillStorage(uid, component); } - private void FillStorage(EntityUid uid, IStorageComponent? storage = null, - SurplusBundleComponent? component = null) + private void FillStorage(EntityUid uid, SurplusBundleComponent? component = null) { - if (!Resolve(uid, ref storage, ref component)) + if (!Resolve(uid, ref component)) return; var cords = Transform(uid).Coordinates; @@ -46,7 +47,7 @@ public sealed class SurplusBundleSystem : EntitySystem foreach (var item in content) { var ent = EntityManager.SpawnEntity(item.ItemId, cords); - storage.Insert(ent); + _entityStorage.Insert(ent, component.Owner); } } diff --git a/Content.Shared/Morgue/SharedMorgue.cs b/Content.Shared/Morgue/SharedMorgue.cs index 640f3b7a8f..4563be36d7 100644 --- a/Content.Shared/Morgue/SharedMorgue.cs +++ b/Content.Shared/Morgue/SharedMorgue.cs @@ -1,19 +1,16 @@ -using Robust.Shared.Serialization; +using Robust.Shared.Serialization; -namespace Content.Shared.Morgue +namespace Content.Shared.Morgue; + +[Serializable, NetSerializable] +public enum MorgueVisuals { - [Serializable, NetSerializable] - public enum MorgueVisuals - { - Open, - HasContents, - HasMob, - HasSoul, - } - - [Serializable, NetSerializable] - public enum CrematoriumVisuals - { - Burning, - } + HasMob, + HasSoul, +} + +[Serializable, NetSerializable] +public enum CrematoriumVisuals +{ + Burning, } diff --git a/Content.Shared/Storage/SharedStorageComponent.cs b/Content.Shared/Storage/SharedStorageComponent.cs index 5921474977..d7ece717be 100644 --- a/Content.Shared/Storage/SharedStorageComponent.cs +++ b/Content.Shared/Storage/SharedStorageComponent.cs @@ -77,6 +77,7 @@ namespace Content.Shared.Storage public enum StorageVisuals : byte { Open, + HasContents, CanLock, Locked } diff --git a/Resources/Locale/en-US/morgue/components/morgue-entity-storage-component.ftl b/Resources/Locale/en-US/morgue/components/morgue-entity-storage-component.ftl index a58533ce8e..b067c3a377 100644 --- a/Resources/Locale/en-US/morgue/components/morgue-entity-storage-component.ftl +++ b/Resources/Locale/en-US/morgue/components/morgue-entity-storage-component.ftl @@ -1,4 +1,3 @@ -morgue-entity-storage-component-cannot-open-no-space = There's no room for the tray to extend! morgue-entity-storage-component-on-examine-details-body-has-soul = The content light is [color=green]green[/color], this body might still be saved! morgue-entity-storage-component-on-examine-details-body-has-no-soul = The content light is [color=red]red[/color], there's a dead body in here! Oh wait... morgue-entity-storage-component-on-examine-details-has-contents = The content light is [color=yellow]yellow[/color], there's something in here. diff --git a/Resources/Locale/en-US/storage/components/entity-storage-component.ftl b/Resources/Locale/en-US/storage/components/entity-storage-component.ftl index 312c7e1d82..69646ebab0 100644 --- a/Resources/Locale/en-US/storage/components/entity-storage-component.ftl +++ b/Resources/Locale/en-US/storage/components/entity-storage-component.ftl @@ -1,6 +1,7 @@ entity-storage-component-welded-shut-message = It's welded completely shut! entity-storage-component-locked-message = It's Locked! entity-storage-component-already-contains-user-message = It's too Cramped! +entity-storage-component-cannot-open-no-space = There's no room to open it! ## OpenToggleVerb diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml index 3c141613c7..c5a64fc63f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml @@ -37,13 +37,14 @@ mass: 5 mask: - Impassable - - type: BodyBagEntityStorage + - type: EntityStorage Capacity: 1 IsCollidableWhenOpen: true closeSound: path: /Audio/Misc/zip.ogg openSound: path: /Audio/Misc/zip.ogg + - type: EntityStorageLayingDownOverride - type: Foldable - type: PaperLabel labelSlot: @@ -58,7 +59,12 @@ state_open: open_overlay - type: FoldableVisualizer key: bag - - type: BodyBagVisualizer + - type: GenericVisualizer + visuals: + enum.PaperLabelVisuals.HasLabel: + enum.BodyBagVisualLayers.Label: + True: {visible: true} + False: {visible: false} - type: Pullable - type: AntiRottingContainer diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml index d79866083d..ea3abe5c31 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml @@ -38,8 +38,9 @@ - type: Icon sprite: Structures/Storage/Crates/artifact.rsi state: artifact_container_icon - - type: ArtifactStorage + - type: EntityStorage Capacity: 1 + - type: ArtifactStorage - type: Weldable - type: SuppressArtifactContainer - type: PlaceableSurface diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/cursed.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/cursed.yml index 573c593fc2..35fd16bac4 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/cursed.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/cursed.yml @@ -6,3 +6,6 @@ description: A standard-issue Nanotrasen storage unit. components: - type: CursedEntityStorage + - type: EntityStorage + closeSound: + path: /Audio/Effects/teleport_arrival.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml index efec0f2224..1eb005cfea 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml @@ -9,6 +9,9 @@ layers: - state: morgue_closed map: ["enum.MorgueVisualLayers.Base"] + - state: morgue_tray + offset: 0, -1 + map: ["enum.StorageVisualLayers.Door"] - state: morgue_nomob_light visible: false map: ["enum.MorgueVisualLayers.Light"] @@ -27,34 +30,41 @@ - MachineMask layer: - WallLayer - - type: MorgueEntityStorage + - type: EntityStorage IsCollidableWhenOpen: true + showContents: false Capacity: 1 + enteringOffset: 0, -1 closeSound: path: /Audio/Items/deconstruct.ogg openSound: path: /Audio/Items/deconstruct.ogg - trayPrototype: MorgueTray + - type: EntityStorageLayingDownOverride + - type: Morgue - type: ContainerContainer containers: entity_storage: !type:Container morgue_tray: !type:ContainerSlot - type: Appearance visuals: - - type: MorgueVisualizer - state_open: morgue_open - state_closed: morgue_closed - light_contents: morgue_nomob_light - light_mob: morgue_nosoul_light - light_soul: morgue_soul_light + - type: StorageVisualizer + state: morgue_closed + state_alt: morgue_open + state_open: morgue_tray + - type: MorgueVisuals + lightContents: morgue_nomob_light + lightMob: morgue_nosoul_light + lightSoul: morgue_soul_light - type: Transform anchored: true - type: AntiRottingContainer +#needs to be removed - type: entity id: MorgueTray name: morgue tray description: If you lay down to have a rest on this, you'll soon have a problem. + noSpawn: true components: - type: Physics bodyType: Static @@ -70,10 +80,6 @@ netsync: false sprite: Structures/Storage/morgue.rsi state: morgue_tray - - type: Clickable - - type: InteractionOutline - - type: MorgueTray - - type: AntiRottingContainer - type: entity id: Crematorium @@ -86,6 +92,9 @@ layers: - state: crema_closed map: ["enum.CrematoriumVisualLayers.Base"] + - state: crema_tray + offset: 0, -1 + map: ["enum.StorageVisualLayers.Door"] - state: crema_contents_light visible: false map: ["enum.CrematoriumVisualLayers.Light"] @@ -104,31 +113,37 @@ - MachineMask layer: - MachineLayer - - type: CrematoriumEntityStorage + - type: EntityStorage IsCollidableWhenOpen: true + showContents: false Capacity: 1 + enteringOffset: 0, -1 closeSound: path: /Audio/Items/deconstruct.ogg openSound: path: /Audio/Items/deconstruct.ogg - trayPrototype: CrematoriumTray - doSoulBeep: false + - type: EntityStorageLayingDownOverride + - type: Crematorium - type: Appearance visuals: - - type: CrematoriumVisualizer - state_open: crema_open - state_closed: crema_closed - light_contents: crema_contents_light - light_burning: crema_active_light + - type: StorageVisualizer + state: crema_closed + state_alt: crema_open + state_open: crema_tray + - type: CrematoriumVisuals + lightContents: crema_contents_light + lightBurning: crema_active_light - type: Transform anchored: true +#needs to be removed - type: entity id: CrematoriumTray name: crematorium tray parent: MorgueTray + noSpawn: true components: - type: Sprite netsync: false sprite: Structures/Storage/morgue.rsi - state: crema_tray + state: crema_tray \ No newline at end of file