diff --git a/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs new file mode 100644 index 0000000000..e4e664f592 --- /dev/null +++ b/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using Content.Shared.Damage; +using Content.Shared.FixedPoint; +using NUnit.Framework; + +namespace Content.IntegrationTests.Tests.Damageable; + +[TestFixture] +[TestOf(typeof(DamageSpecifier))] +public class DamageSpecifierTest : ContentIntegrationTest +{ + [Test] + public void TestDamageSpecifierOperations() + { + // Test basic math operations. + // I've already nearly broken these once. When editing the operators. + + DamageSpecifier input1 = new() { DamageDict = _input1 }; + DamageSpecifier input2 = new() { DamageDict = _input2 }; + DamageSpecifier output1 = new() { DamageDict = _output1 }; + DamageSpecifier output2 = new() { DamageDict = _output2 }; + DamageSpecifier output3 = new() { DamageDict = _output3 }; + DamageSpecifier output4 = new() { DamageDict = _output4 }; + DamageSpecifier output5 = new() { DamageDict = _output5 }; + + Assert.That((-input1).Equals(output1)); + Assert.That((input1 / 2).Equals(output2)); + Assert.That((input1 * 2).Equals(output3)); + + var difference = (input1 - input2); + Assert.That(difference.Equals(output4)); + + var difference2 = (-input2) + input1; + Assert.That(difference.Equals(difference2)); + + difference.Clamp(-0.25f, 0.25f); + Assert.That(difference.Equals(output5)); + } + + static Dictionary _input1 = new() + { + { "A", 1.5f }, + { "B", 2 }, + { "C", 3 } + }; + + static Dictionary _input2 = new() + { + { "A", 1 }, + { "B", 2 }, + { "C", 5 }, + { "D", 0.05f } + }; + + static Dictionary _output1 = new() + { + { "A", -1.5f }, + { "B", -2 }, + { "C", -3 } + }; + + static Dictionary _output2 = new() + { + { "A", 0.75f }, + { "B", 1 }, + { "C", 1.5 } + }; + + static Dictionary _output3 = new() + { + { "A", 3f }, + { "B", 4 }, + { "C", 6 } + }; + + static Dictionary _output4 = new() + { + { "A", 0.5f }, + { "B", 0 }, + { "C", -2 }, + { "D", -0.05f } + }; + + static Dictionary _output5 = new() + { + { "A", 0.25f }, + { "B", 0 }, + { "C", -0.25f }, + { "D", -0.05f } + }; +} diff --git a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs index ffee77a16c..ffc727da75 100644 --- a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs @@ -51,9 +51,6 @@ namespace Content.Server.Atmos.EntitySystems } SetAirblocked(airtight, false, xform); - - InvalidatePosition(airtight.LastPosition.Item1, airtight.LastPosition.Item2, airtight.FixVacuum); - RaiseLocalEvent(new AirtightChanged(airtight)); } private void OnAirtightPositionChanged(EntityUid uid, AirtightComponent airtight, ref AnchorStateChangedEvent args) diff --git a/Content.Shared/Damage/DamageSpecifier.cs b/Content.Shared/Damage/DamageSpecifier.cs index 50fed1c687..91cbea8619 100644 --- a/Content.Shared/Damage/DamageSpecifier.cs +++ b/Content.Shared/Damage/DamageSpecifier.cs @@ -22,7 +22,7 @@ namespace Content.Shared.Damage /// functions to apply resistance sets and supports basic math operations to modify this dictionary. /// [DataDefinition] - public class DamageSpecifier + public class DamageSpecifier : IEquatable { [JsonPropertyName("types")] [DataField("types", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] @@ -384,7 +384,21 @@ namespace Content.Shared.Damage return newDamage; } - public static DamageSpecifier operator -(DamageSpecifier damageSpecA, DamageSpecifier damageSpecB) => damageSpecA + -damageSpecB; + // Here we define the subtraction operator explicitly, rather than implicitly via something like X + (-1 * Y). + // This is faster because FixedPoint2 multiplication is somewhat involved. + public static DamageSpecifier operator -(DamageSpecifier damageSpecA, DamageSpecifier damageSpecB) + { + DamageSpecifier newDamage = new(damageSpecA); + + foreach (var entry in damageSpecB.DamageDict) + { + if (!newDamage.DamageDict.TryAdd(entry.Key, -entry.Value)) + { + newDamage.DamageDict[entry.Key] -= entry.Value; + } + } + return newDamage; + } public static DamageSpecifier operator +(DamageSpecifier damageSpec) => damageSpec; @@ -393,6 +407,20 @@ namespace Content.Shared.Damage public static DamageSpecifier operator *(float factor, DamageSpecifier damageSpec) => damageSpec * factor; public static DamageSpecifier operator *(FixedPoint2 factor, DamageSpecifier damageSpec) => damageSpec * factor; + + public bool Equals(DamageSpecifier? other) + { + if (other == null || DamageDict.Count != other.DamageDict.Count) + return false; + + foreach (var (key, value) in DamageDict) + { + if (!other.DamageDict.TryGetValue(key, out var otherValue) || value != otherValue) + return false; + } + + return true; + } } #endregion } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index c3f02a2085..64b973f892 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -84,7 +84,7 @@ namespace Content.Shared.Damage { component.DamagePerGroup = component.Damage.GetDamagePerGroup(); component.TotalDamage = component.Damage.Total; - component.Dirty(); + Dirty(component); if (EntityManager.TryGetComponent(component.Owner, out var appearance) && damageDelta != null) { diff --git a/Content.Shared/FixedPoint/FixedPoint2.cs b/Content.Shared/FixedPoint/FixedPoint2.cs index 74d95df587..e6a3dcdb53 100644 --- a/Content.Shared/FixedPoint/FixedPoint2.cs +++ b/Content.Shared/FixedPoint/FixedPoint2.cs @@ -67,7 +67,7 @@ namespace Content.Shared.FixedPoint => new(a._value + b._value); public static FixedPoint2 operator -(FixedPoint2 a, FixedPoint2 b) - => a + -b; + => new(a._value - b._value); public static FixedPoint2 operator *(FixedPoint2 a, FixedPoint2 b) { diff --git a/Content.Shared/Pulling/Components/PullableComponent.cs b/Content.Shared/Pulling/Components/PullableComponent.cs index f6029b91ca..7afa3ad121 100644 --- a/Content.Shared/Pulling/Components/PullableComponent.cs +++ b/Content.Shared/Pulling/Components/PullableComponent.cs @@ -29,7 +29,7 @@ namespace Content.Shared.Pulling.Components /// public DistanceJoint? PullJoint { get; set; } - public bool BeingPulled => Puller != default; + public bool BeingPulled => Puller != null; public EntityCoordinates? MovingTo { get; set; } @@ -76,15 +76,9 @@ namespace Content.Shared.Pulling.Components EntitySystem.Get().ForceRelationship(comp, this); } - protected override void Shutdown() - { - EntitySystem.Get().ForceDisconnectPullable(this); - base.Shutdown(); - } - protected override void OnRemove() { - if (Puller != default) + if (Puller != null) { // This is absolute paranoia but it's also absolutely necessary. Too many puller state bugs. - 20kdc Logger.ErrorS("c.go.c.pulling", "PULLING STATE CORRUPTION IMMINENT IN PULLABLE {0} - OnRemove called when Puller is set!", Owner); diff --git a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs index 3d0d76be35..1bb301d431 100644 --- a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs +++ b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs @@ -31,6 +31,19 @@ namespace Content.Shared.Pulling { [Dependency] private readonly SharedJointSystem _jointSystem = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShutdown); + } + + private void OnShutdown(EntityUid uid, SharedPullableComponent component, ComponentShutdown args) + { + if (component.Puller != null) + ForceRelationship(null, component); + } + // A WARNING: // The following 2 functions are the most internal part of the pulling system's relationship management. // They do not expect to be cancellable.