diff --git a/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs b/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs index 8503644d77..bd292db23f 100644 --- a/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs +++ b/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs @@ -35,6 +35,8 @@ namespace Content.Client.GameObjects.Components.Mobs [ViewVariables] private Dictionary _cooldown = new Dictionary(); + public override IReadOnlyDictionary Statuses => _status; + /// /// Allows calculating if we need to act due to this component being controlled by the current mob /// diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs new file mode 100644 index 0000000000..3e128bceee --- /dev/null +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -0,0 +1,96 @@ +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Gravity; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Gravity; +using Content.Shared.GameObjects.Components.Mobs; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Utility; +using NUnit.Framework; +using Robust.Server.Interfaces.Timing; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Tests.Gravity +{ + [TestFixture] + [TestOf(typeof(WeightlessSystem))] + [TestOf(typeof(GravityGeneratorComponent))] + public class WeightlessStatusTests : ContentIntegrationTest + { + [Test] + public async Task WeightlessStatusTest() + { + var server = StartServer(); + + await server.WaitIdleAsync(); + + var mapManager = server.ResolveDependency(); + var entityManager = server.ResolveDependency(); + var pauseManager = server.ResolveDependency(); + var tileDefinitionManager = server.ResolveDependency(); + + IEntity human = null; + SharedStatusEffectsComponent statusEffects = null; + + await server.WaitAssertion(() => + { + var mapId = mapManager.CreateMap(); + + pauseManager.AddUninitializedMap(mapId); + + var gridId = new GridId(1); + + if (!mapManager.TryGetGrid(gridId, out var grid)) + { + grid = mapManager.CreateGrid(mapId, gridId); + } + + var tileDefinition = tileDefinitionManager["underplating"]; + var tile = new Tile(tileDefinition.TileId); + var coordinates = grid.ToCoordinates(); + + grid.SetTile(coordinates, tile); + + pauseManager.DoMapInitialize(mapId); + + human = entityManager.SpawnEntity("HumanMob_Content", coordinates); + + Assert.True(human.TryGetComponent(out statusEffects)); + }); + + // Let WeightlessSystem and GravitySystem tick + await server.WaitRunTicks(1); + + GravityGeneratorComponent gravityGenerator = null; + + await server.WaitAssertion(() => + { + // No gravity without a gravity generator + Assert.True(statusEffects.Statuses.ContainsKey(StatusEffect.Weightless)); + + gravityGenerator = human.EnsureComponent(); + }); + + // Let WeightlessSystem and GravitySystem tick + await server.WaitRunTicks(1); + + await server.WaitAssertion(() => + { + Assert.False(statusEffects.Statuses.ContainsKey(StatusEffect.Weightless)); + + // Disable the gravity generator + var args = new BreakageEventArgs {Owner = human}; + gravityGenerator.OnBreak(args); + }); + + await server.WaitRunTicks(1); + + await server.WaitAssertion(() => + { + Assert.False(statusEffects.Statuses.ContainsKey(StatusEffect.Weightless)); + }); + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/ServerStatusEffectsComponent.cs b/Content.Server/GameObjects/Components/Mobs/ServerStatusEffectsComponent.cs index 38d64383ad..8715fdb3d7 100644 --- a/Content.Server/GameObjects/Components/Mobs/ServerStatusEffectsComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/ServerStatusEffectsComponent.cs @@ -23,6 +23,8 @@ namespace Content.Server.GameObjects.Components.Mobs [ViewVariables] private readonly Dictionary _statusEffects = new Dictionary(); + public override IReadOnlyDictionary Statuses => _statusEffects; + protected override void Startup() { base.Startup(); diff --git a/Content.Server/GameObjects/EntitySystems/WeightlessSystem.cs b/Content.Server/GameObjects/EntitySystems/WeightlessSystem.cs index 885e373563..1ceec26a0a 100644 --- a/Content.Server/GameObjects/EntitySystems/WeightlessSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/WeightlessSystem.cs @@ -7,6 +7,8 @@ using JetBrains.Annotations; using Robust.Shared.GameObjects.Components.Map; using Robust.Shared.GameObjects.EntitySystemMessages; using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Utility; @@ -15,6 +17,8 @@ namespace Content.Server.GameObjects.EntitySystems [UsedImplicitly] public class WeightlessSystem : EntitySystem, IResettingEntitySystem { + [Dependency] private readonly IMapManager _mapManager = default!; + private readonly Dictionary> _statuses = new Dictionary>(); public override void Initialize() @@ -32,10 +36,22 @@ namespace Content.Server.GameObjects.EntitySystems public void AddStatus(ServerStatusEffectsComponent status) { - var grid = status.Owner.Transform.GridID; - var statuses = _statuses.GetOrNew(grid); + var gridId = status.Owner.Transform.GridID; + var statuses = _statuses.GetOrNew(gridId); statuses.Add(status); + + if (_mapManager.TryGetGrid(status.Owner.Transform.GridID, out var grid)) + { + if (grid.HasGravity) + { + RemoveWeightless(status); + } + else + { + AddWeightless(status); + } + } } public void RemoveStatus(ServerStatusEffectsComponent status) diff --git a/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs b/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs index 68c6b41d2e..2c1cffeadd 100644 --- a/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs +++ b/Content.Shared/GameObjects/Components/Mobs/SharedStatusEffectsComponent.cs @@ -14,6 +14,8 @@ namespace Content.Shared.GameObjects.Components.Mobs public override string Name => "StatusEffectsUI"; public override uint? NetID => ContentNetIDs.STATUSEFFECTS; + public abstract IReadOnlyDictionary Statuses { get; } + public abstract void ChangeStatusEffectIcon(StatusEffect effect, string icon); public abstract void ChangeStatusEffect(StatusEffect effect, string icon, ValueTuple? cooldown);