diff --git a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs new file mode 100644 index 0000000000..f73009c28d --- /dev/null +++ b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs @@ -0,0 +1,155 @@ +using System.Linq; +using System.Threading.Tasks; +using Content.Server.GameObjects.Components; +using NUnit.Framework; +using Robust.Client.GameObjects; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; + +namespace Content.IntegrationTests.Tests +{ + public class ContainerOcclusionTest : ContentIntegrationTest + { + private const string ExtraPrototypes = @" +- type: entity + id: ContainerOcclusionA + components: + - type: EntityStorage + occludesLight: true + +- type: entity + id: ContainerOcclusionB + components: + - type: EntityStorage + showContents: true + occludesLight: false + +- type: entity + id: ContainerOcclusionDummy + components: + - type: Sprite + - type: PointLight +"; + + private async Task<(ClientIntegrationInstance c, ServerIntegrationInstance s)> Start() + { + var optsServer = new ServerIntegrationOptions {ExtraPrototypes = ExtraPrototypes}; + var optsClient = new ClientIntegrationOptions {ExtraPrototypes = ExtraPrototypes}; + + var (c, s) = await StartConnectedServerDummyTickerClientPair(optsClient, optsServer); + + s.Post(() => + { + IoCManager.Resolve() + .GetAllPlayers().Single() + .JoinGame(); + + var mapMan = IoCManager.Resolve(); + + mapMan.CreateMap(new MapId(1)); + }); + + return (c, s); + } + + [Test] + public async Task TestA() + { + var (c, s) = await Start(); + + EntityUid dummyUid = default; + s.Post(() => + { + var pos = new MapCoordinates(Vector2.Zero, new MapId(1)); + var ent = IoCManager.Resolve(); + var container = ent.SpawnEntity("ContainerOcclusionA", pos); + var dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos); + dummyUid = dummy.Uid; + + container.GetComponent().Insert(dummy); + }); + + await RunTicksSync(c, s, 5); + + c.Assert(() => + { + var dummy = IoCManager.Resolve().GetEntity(dummyUid); + var sprite = dummy.GetComponent(); + var light = dummy.GetComponent(); + Assert.True(sprite.ContainerOccluded); + Assert.True(light.ContainerOccluded); + }); + + await Task.WhenAll(c.WaitIdleAsync(), s.WaitIdleAsync()); + } + + [Test] + public async Task TestB() + { + var (c, s) = await Start(); + + EntityUid dummyUid = default; + s.Post(() => + { + var pos = new MapCoordinates(Vector2.Zero, new MapId(1)); + var ent = IoCManager.Resolve(); + var container = ent.SpawnEntity("ContainerOcclusionB", pos); + var dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos); + dummyUid = dummy.Uid; + + container.GetComponent().Insert(dummy); + }); + + await RunTicksSync(c, s, 5); + + c.Assert(() => + { + var dummy = IoCManager.Resolve().GetEntity(dummyUid); + var sprite = dummy.GetComponent(); + var light = dummy.GetComponent(); + Assert.False(sprite.ContainerOccluded); + Assert.False(light.ContainerOccluded); + }); + + await Task.WhenAll(c.WaitIdleAsync(), s.WaitIdleAsync()); + } + + [Test] + public async Task TestAb() + { + var (c, s) = await Start(); + + EntityUid dummyUid = default; + s.Post(() => + { + var pos = new MapCoordinates(Vector2.Zero, new MapId(1)); + var ent = IoCManager.Resolve(); + var containerA = ent.SpawnEntity("ContainerOcclusionA", pos); + var containerB = ent.SpawnEntity("ContainerOcclusionB", pos); + var dummy = ent.SpawnEntity("ContainerOcclusionDummy", pos); + dummyUid = dummy.Uid; + + containerA.GetComponent().Insert(containerB); + containerB.GetComponent().Insert(dummy); + }); + + await RunTicksSync(c, s, 5); + + c.Assert(() => + { + var dummy = IoCManager.Resolve().GetEntity(dummyUid); + var sprite = dummy.GetComponent(); + var light = dummy.GetComponent(); + Assert.True(sprite.ContainerOccluded); + Assert.True(light.ContainerOccluded); + }); + + await Task.WhenAll(c.WaitIdleAsync(), s.WaitIdleAsync()); + } + } +} diff --git a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs index bc4e71573b..5e88608124 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs @@ -49,6 +49,7 @@ namespace Content.Server.GameObjects.Components [ViewVariables] private IEntityQuery _entityQuery; private bool _showContents; + private bool _occludesLight; private bool _open; private bool _isWeldedShut; private int _collisionMaskStorage; @@ -68,6 +69,17 @@ namespace Content.Server.GameObjects.Components } } + [ViewVariables(VVAccess.ReadWrite)] + public bool OccludesLight + { + get => _occludesLight; + set + { + _occludesLight = value; + _contents.OccludesLight = _occludesLight; + } + } + [ViewVariables(VVAccess.ReadWrite)] public bool Open { @@ -101,6 +113,7 @@ namespace Content.Server.GameObjects.Components _entityQuery = new IntersectingEntityQuery(Owner); _contents.ShowContents = _showContents; + _contents.OccludesLight = _occludesLight; if (Owner.TryGetComponent(out var placeableSurfaceComponent)) { @@ -116,6 +129,7 @@ namespace Content.Server.GameObjects.Components serializer.DataField(ref _storageCapacityMax, "Capacity", 30); serializer.DataField(ref _isCollidableWhenOpen, "IsCollidableWhenOpen", false); serializer.DataField(ref _showContents, "showContents", false); + serializer.DataField(ref _occludesLight, "occludesLight", true); serializer.DataField(ref _open, "open", false); serializer.DataField(this, a => a.IsWeldedShut, "IsWeldedShut", false); serializer.DataField(this, a => a.CanWeldShut, "CanWeldShut", true); diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 18ce9920e4..6a98fb715e 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.GUI; -using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Utility; @@ -46,6 +45,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage private Container? _storage; + private bool _occludesLight; private bool _storageInitialCalculated; private int _storageUsed; private int _storageCapacityMax; @@ -53,6 +53,16 @@ namespace Content.Server.GameObjects.Components.Items.Storage public IReadOnlyCollection? StoredEntities => _storage?.ContainedEntities; + public bool OccludesLight + { + get => _occludesLight; + set + { + _occludesLight = value; + if (_storage != null) _storage.OccludesLight = value; + } + } + private void EnsureInitialCalculated() { if (_storageInitialCalculated) @@ -318,6 +328,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage // ReSharper disable once StringLiteralTypo _storage = ContainerManagerComponent.Ensure("storagebase", Owner); + _storage.OccludesLight = _occludesLight; } public override void ExposeData(ObjectSerializer serializer) @@ -325,6 +336,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage base.ExposeData(serializer); serializer.DataField(ref _storageCapacityMax, "capacity", 10000); + serializer.DataField(ref _occludesLight, "occludesLight", true); //serializer.DataField(ref StorageUsed, "used", 0); }