Generalize ReagentUnit into FixedPoint2 and use it for damage calculations (#5151)

* Damage units

* sum ext method
This commit is contained in:
mirrorcult
2021-11-03 16:48:03 -07:00
committed by GitHub
parent 8165d8f38c
commit 3ab4a30a0f
100 changed files with 730 additions and 601 deletions

View File

@@ -1,5 +1,6 @@
using System.Reflection;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -9,18 +10,18 @@ using Robust.UnitTesting.Shared.Serialization;
namespace Content.IntegrationTests.Tests.Chemistry
{
public class ReagentUnitSerializationTest : SerializationTest
public class FixedPoint2SerializationTest : SerializationTest
{
protected override Assembly[] Assemblies => new[]
{
typeof(ReagentUnitSerializationTest).Assembly
typeof(FixedPoint2SerializationTest).Assembly
};
[Test]
public void DeserializeNullTest()
{
var node = new ValueDataNode("null");
var unit = Serialization.ReadValue<ReagentUnit?>(node);
var unit = Serialization.ReadValue<FixedPoint2?>(node);
Assert.That(unit, Is.Null);
}
@@ -29,15 +30,15 @@ namespace Content.IntegrationTests.Tests.Chemistry
public void DeserializeNullDefinitionTest()
{
var node = new MappingDataNode().Add("unit", "null");
var definition = Serialization.ReadValueOrThrow<ReagentUnitTestDefinition>(node);
var definition = Serialization.ReadValueOrThrow<FixedPoint2TestDefinition>(node);
Assert.That(definition.Unit, Is.Null);
}
}
[DataDefinition]
public class ReagentUnitTestDefinition
public class FixedPoint2TestDefinition
{
[DataField("unit")] public ReagentUnit? Unit { get; set; } = ReagentUnit.New(5);
[DataField("unit")] public FixedPoint2? Unit { get; set; } = FixedPoint2.New(5);
}
}

View File

@@ -2,6 +2,7 @@ using System.Threading.Tasks;
using Content.Server.Administration.Commands;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.MobState;
using NUnit.Framework;
using Robust.Shared.GameObjects;
@@ -56,7 +57,8 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(mobState.IsIncapacitated, Is.False);
// Kill the entity
DamageSpecifier damage = new(prototypeManager.Index<DamageGroupPrototype>("Toxin"), 10000000);
DamageSpecifier damage = new(prototypeManager.Index<DamageGroupPrototype>("Toxin"),
FixedPoint2.New(10000000));
EntitySystem.Get<DamageableSystem>().TryChangeDamage(human.Uid, damage, true);
// Check that it is dead
@@ -74,7 +76,7 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(mobState.IsDead, Is.False);
Assert.That(mobState.IsIncapacitated, Is.False);
Assert.That(damageable.TotalDamage, Is.Zero);
Assert.That(damageable.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
});
}
}

View File

@@ -2,6 +2,7 @@ using System.Linq;
using System.Threading.Tasks;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
@@ -108,7 +109,7 @@ namespace Content.IntegrationTests.Tests.Damageable
DamageTypePrototype type3b = default!;
DamageTypePrototype type3c = default!;
int typeDamage, groupDamage;
FixedPoint2 typeDamage, groupDamage;
await server.WaitPost(() =>
{
@@ -148,7 +149,7 @@ namespace Content.IntegrationTests.Tests.Damageable
// Check that damage is evenly distributed over a group if its a nice multiple
var types = group3.DamageTypes;
var damageToDeal = types.Count() * 5;
var damageToDeal = FixedPoint2.New(types.Count() * 5);
DamageSpecifier damage = new(group3, damageToDeal);
sDamageableSystem.TryChangeDamage(uid, damage, true);
@@ -166,73 +167,74 @@ namespace Content.IntegrationTests.Tests.Damageable
sDamageableSystem.TryChangeDamage(uid, -damage);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.Zero));
foreach (var type in types)
{
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage));
Assert.That(typeDamage, Is.Zero);
Assert.That(typeDamage, Is.EqualTo(FixedPoint2.Zero));
}
// Check that damage works properly if it is NOT perfectly divisible among group members
types = group3.DamageTypes;
damageToDeal = types.Count() * 5 - 1;
damageToDeal = FixedPoint2.New(types.Count() * 5 - 1);
damage = new DamageSpecifier(group3, damageToDeal);
sDamageableSystem.TryChangeDamage(uid, damage, true);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(damageToDeal));
// integer rounding. In this case, first member gets 1 less than others.
Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(damageToDeal / types.Count()));
Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(1 + damageToDeal / types.Count()));
Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(1 + damageToDeal / types.Count()));
Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(damageToDeal / types.Count()));
// last one will get 0.01 less, since its not perfectly divisble by 3
Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(damageToDeal / types.Count() - 0.01));
// Heal
sDamageableSystem.TryChangeDamage(uid, -damage);
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.Zero));
foreach (var type in types)
{
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage));
Assert.That(typeDamage, Is.Zero);
Assert.That(typeDamage, Is.EqualTo(FixedPoint2.Zero));
}
// Test that unsupported groups return false when setting/getting damage (and don't change damage)
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
damage = new DamageSpecifier(group1, 10) + new DamageSpecifier(type2b, 10);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
damage = new DamageSpecifier(group1, FixedPoint2.New(10)) + new DamageSpecifier(type2b, FixedPoint2.New(10));
sDamageableSystem.TryChangeDamage(uid, damage, true);
Assert.That(DamageChanged, Is.False);
Assert.That(sDamageableComponent.DamagePerGroup.TryGetValue(group1.ID, out groupDamage), Is.False);
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type1.ID, out typeDamage), Is.False);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
// Test SetAll function
sDamageableSystem.SetAllDamage(sDamageableComponent, 10);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(10 * sDamageableComponent.Damage.DamageDict.Count()));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(10 * sDamageableComponent.Damage.DamageDict.Count())));
sDamageableSystem.SetAllDamage(sDamageableComponent, 0);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
// Test 'wasted' healing
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3a, 5));
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3b, 7));
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -11));
Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(2));
Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(3));
Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(0));
Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(FixedPoint2.New(1.33)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(FixedPoint2.New(3.33)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(FixedPoint2.New(0)));
// Test Over-Healing
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100));
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)));
Assert.That(DamageChanged);
DamageChanged = false;
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
// Test that if no health change occurred, returns false
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100));
Assert.That(DamageChanged, Is.False);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
});
}
}

View File

@@ -2,6 +2,7 @@ using System.Threading.Tasks;
using Content.Server.Destructible.Thresholds.Triggers;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -60,8 +61,8 @@ namespace Content.IntegrationTests.Tests.Destructible
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBurn");
DamageSpecifier bruteDamage = new(bruteDamageGroup,5);
DamageSpecifier burnDamage = new(burnDamageGroup,5);
DamageSpecifier bruteDamage = new(bruteDamageGroup, FixedPoint2.New(5));
DamageSpecifier burnDamage = new(burnDamageGroup, FixedPoint2.New(5));
// Raise brute damage to 5
sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage, true);
@@ -112,7 +113,7 @@ namespace Content.IntegrationTests.Tests.Destructible
// Lower brute damage to 0
sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage * -10);
Assert.That(sDamageableComponent.TotalDamage,Is.EqualTo(20));
Assert.That(sDamageableComponent.TotalDamage,Is.EqualTo(FixedPoint2.New(20)));
// No new thresholds reached, healing should not trigger it
Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached);

View File

@@ -6,6 +6,7 @@ using Content.Server.Destructible.Thresholds.Behaviors;
using Content.Server.Destructible.Thresholds.Triggers;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -186,7 +187,7 @@ namespace Content.IntegrationTests.Tests.Destructible
sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*5, true);
// Check that the total damage matches
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50)));
// Both thresholds should have triggered
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Exactly(2).Items);
@@ -234,7 +235,7 @@ namespace Content.IntegrationTests.Tests.Destructible
sDamageableSystem.SetAllDamage(sDamageableComponent, 0);
// Check that the entity has 0 damage
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
// Set both thresholds to only trigger once
foreach (var destructibleThreshold in sDestructibleComponent.Thresholds)
@@ -247,7 +248,7 @@ namespace Content.IntegrationTests.Tests.Destructible
sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*5, true);
// Check that the total damage matches
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50)));
// No thresholds should have triggered as they were already triggered before, and they are set to only trigger once
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
@@ -260,7 +261,7 @@ namespace Content.IntegrationTests.Tests.Destructible
}
// Check that the total damage matches
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50)));
// They shouldn't have been triggered by changing TriggersOnce
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);

View File

@@ -4,6 +4,7 @@ using Content.Server.Fluids.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Coordinates;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
@@ -56,7 +57,7 @@ namespace Content.IntegrationTests.Tests.Fluids
server.Assert(() =>
{
var solution = new Solution("water", ReagentUnit.New(20));
var solution = new Solution("water", FixedPoint2.New(20));
var puddle = solution.SpillAt(coordinates, "PuddleSmear");
Assert.NotNull(puddle);
});
@@ -94,7 +95,7 @@ namespace Content.IntegrationTests.Tests.Fluids
server.Assert(() =>
{
var coordinates = grid.ToCoordinates();
var solution = new Solution("water", ReagentUnit.New(20));
var solution = new Solution("water", FixedPoint2.New(20));
var puddle = solution.SpillAt(coordinates, "PuddleSmear");
Assert.Null(puddle);
});
@@ -154,7 +155,7 @@ namespace Content.IntegrationTests.Tests.Fluids
// Spawn a puddle
await server.WaitAssertion(() =>
{
var solution = new Solution("water", ReagentUnit.New(amount));
var solution = new Solution("water", FixedPoint2.New(amount));
puddle = solution.SpillAt(sCoordinates, "PuddleSmear");
// Check that the puddle was created