Fix healing damage classes and damageable serialization and add test (#2727)
* Fix healing damage classes and damageable serialization and add test * The fall of an empire * Fix healPerType being -1 instead of 1
This commit is contained in:
151
Content.IntegrationTests/Tests/Damageable/DamageTest.cs
Normal file
151
Content.IntegrationTests/Tests/Damageable/DamageTest.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Damageable
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(DamageableComponent))]
|
||||
public class DamageTest : ContentIntegrationTest
|
||||
{
|
||||
private static readonly string DamageableEntityId = "DamageableEntityId";
|
||||
|
||||
private static readonly string Prototypes = $@"
|
||||
- type: entity
|
||||
id: {DamageableEntityId}
|
||||
name: {DamageableEntityId}
|
||||
components:
|
||||
- type: Damageable
|
||||
damageContainer: allDamageContainer";
|
||||
|
||||
[Test]
|
||||
public async Task TestDamageTypeDamageAndHeal()
|
||||
{
|
||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||
{
|
||||
ExtraPrototypes = Prototypes
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
|
||||
IEntity sDamageableEntity = null;
|
||||
IDamageableComponent sDamageableComponent = null;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapId = sMapManager.NextMapId();
|
||||
var coordinates = new MapCoordinates(0, 0, mapId);
|
||||
sMapManager.CreateMap(mapId);
|
||||
|
||||
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||
|
||||
var damageToDeal = 7;
|
||||
|
||||
foreach (var type in Enum.GetValues<DamageType>())
|
||||
{
|
||||
Assert.That(sDamageableComponent.SupportsDamageType(type));
|
||||
|
||||
// Damage
|
||||
Assert.That(sDamageableComponent.ChangeDamage(type, damageToDeal, true), Is.True);
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
||||
Assert.That(sDamageableComponent.TryGetDamage(type, out var damage), Is.True);
|
||||
Assert.That(damage, Is.EqualTo(damageToDeal));
|
||||
|
||||
// Heal
|
||||
Assert.That(sDamageableComponent.ChangeDamage(type, -damageToDeal, true), Is.True);
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
||||
Assert.That(sDamageableComponent.TryGetDamage(type, out damage), Is.True);
|
||||
Assert.That(damage, Is.Zero);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestDamageClassDamageAndHeal()
|
||||
{
|
||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||
{
|
||||
ExtraPrototypes = Prototypes
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
|
||||
IEntity sDamageableEntity = null;
|
||||
IDamageableComponent sDamageableComponent = null;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapId = sMapManager.NextMapId();
|
||||
var coordinates = new MapCoordinates(0, 0, mapId);
|
||||
sMapManager.CreateMap(mapId);
|
||||
|
||||
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||
|
||||
foreach (var @class in Enum.GetValues<DamageClass>())
|
||||
{
|
||||
Assert.That(sDamageableComponent.SupportsDamageClass(@class));
|
||||
|
||||
var types = @class.ToTypes();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
Assert.That(sDamageableComponent.SupportsDamageType(type));
|
||||
}
|
||||
|
||||
var damageToDeal = types.Count * 5;
|
||||
|
||||
// Damage
|
||||
Assert.That(sDamageableComponent.ChangeDamage(@class, damageToDeal, true), Is.True);
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
||||
Assert.That(sDamageableComponent.TryGetDamage(@class, out var classDamage), Is.True);
|
||||
Assert.That(classDamage, Is.EqualTo(damageToDeal));
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True);
|
||||
Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count));
|
||||
}
|
||||
|
||||
// Heal
|
||||
Assert.That(sDamageableComponent.ChangeDamage(@class, -damageToDeal, true), Is.True);
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
||||
Assert.That(sDamageableComponent.TryGetDamage(@class, out classDamage), Is.True);
|
||||
Assert.That(classDamage, Is.Zero);
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True);
|
||||
Assert.That(typeDamage, Is.Zero);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace Content.Shared.Damage.DamageContainer
|
||||
[Serializable, NetSerializable]
|
||||
public class DamageContainerPrototype : IPrototype, IIndexedPrototype
|
||||
{
|
||||
private bool _supportAll;
|
||||
private HashSet<DamageClass> _supportedClasses;
|
||||
private HashSet<DamageType> _supportedTypes;
|
||||
private string _id;
|
||||
@@ -31,18 +32,26 @@ namespace Content.Shared.Damage.DamageContainer
|
||||
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||
|
||||
serializer.DataField(ref _id, "id", string.Empty);
|
||||
serializer.DataField(ref _supportAll, "supportAll", false);
|
||||
serializer.DataField(ref _supportedClasses, "supportedClasses", new HashSet<DamageClass>());
|
||||
serializer.DataField(ref _supportedTypes, "supportedTypes", new HashSet<DamageType>());
|
||||
|
||||
var originalTypes = _supportedTypes.ToArray();
|
||||
if (_supportAll)
|
||||
{
|
||||
_supportedClasses.UnionWith(Enum.GetValues<DamageClass>());
|
||||
_supportedTypes.UnionWith(Enum.GetValues<DamageType>());
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var supportedClass in _supportedClasses)
|
||||
{
|
||||
foreach (var supportedType in supportedClass.ToTypes())
|
||||
{
|
||||
_supportedTypes.Add(supportedType);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var originalType in originalTypes)
|
||||
foreach (var originalType in _supportedTypes)
|
||||
{
|
||||
_supportedClasses.Add(originalType.ToClass());
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace Content.Shared.Damage
|
||||
var classes = DamageClassExtensions.ToDictionary();
|
||||
|
||||
foreach (var @class in classes.Keys.ToList())
|
||||
{
|
||||
foreach (var type in @class.ToTypes())
|
||||
{
|
||||
if (!types.TryGetValue(type, out var damage))
|
||||
@@ -67,6 +68,7 @@ namespace Content.Shared.Damage
|
||||
|
||||
classes[@class] += damage;
|
||||
}
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Content.Shared.GameObjects.Components.Damage
|
||||
|
||||
// TODO DAMAGE Serialize damage done and resistance changes
|
||||
serializer.DataReadWriteFunction(
|
||||
"damagePrototype",
|
||||
"damageContainer",
|
||||
DefaultDamageContainer,
|
||||
prototype =>
|
||||
{
|
||||
@@ -151,7 +151,7 @@ namespace Content.Shared.GameObjects.Components.Damage
|
||||
() => DamageContainerId);
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"resistancePrototype",
|
||||
"resistances",
|
||||
DefaultResistanceSet,
|
||||
prototype =>
|
||||
{
|
||||
@@ -343,7 +343,7 @@ namespace Content.Shared.GameObjects.Components.Damage
|
||||
{
|
||||
// Changing multiple types is a bit more complicated. Might be a better way (formula?) to do this,
|
||||
// but essentially just loops between each damage category until all healing is used up.
|
||||
var healingLeft = amount;
|
||||
var healingLeft = -amount;
|
||||
var healThisCycle = 1;
|
||||
|
||||
// While we have healing left...
|
||||
@@ -354,11 +354,11 @@ namespace Content.Shared.GameObjects.Components.Damage
|
||||
healThisCycle = 0;
|
||||
|
||||
int healPerType;
|
||||
if (healingLeft > -types.Count)
|
||||
if (healingLeft < types.Count)
|
||||
{
|
||||
// Say we were to distribute 2 healing between 3
|
||||
// this will distribute 1 to each (and stop after 2 are given)
|
||||
healPerType = -1;
|
||||
healPerType = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -369,10 +369,11 @@ namespace Content.Shared.GameObjects.Components.Damage
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var healAmount =
|
||||
Math.Max(Math.Max(healPerType, -GetDamage(type)), healingLeft);
|
||||
var damage = GetDamage(type);
|
||||
var healAmount = Math.Min(healingLeft, damage);
|
||||
healAmount = Math.Min(healAmount, healPerType);
|
||||
|
||||
ChangeDamage(type, healAmount, true);
|
||||
ChangeDamage(type, -healAmount, true);
|
||||
healThisCycle += healAmount;
|
||||
healingLeft -= healAmount;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ namespace Content.Shared.GameObjects.Components.Damage
|
||||
/// </returns>
|
||||
bool TryGetDamage(DamageType type, out int damage);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of damage of a class.
|
||||
/// </summary>
|
||||
/// <param name="class">The class to get the damage of.</param>
|
||||
/// <param name="damage">The amount of damage of that class.</param>
|
||||
/// <returns>
|
||||
/// True if the given <see cref="@class"/> is supported, false otherwise.
|
||||
/// </returns>
|
||||
bool TryGetDamage(DamageClass @class, out int damage);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the specified <see cref="DamageType"/>, applying
|
||||
/// resistance values only if it is damage.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
- type: damageContainer
|
||||
id: allDamageContainer
|
||||
supportAll: true
|
||||
|
||||
- type: damageContainer
|
||||
id: biologicalDamageContainer
|
||||
supportedClasses:
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
preset: HumanPreset
|
||||
centerSlot: torso
|
||||
- type: Damageable
|
||||
damagePrototype: biologicalDamageContainer
|
||||
damageContainer: biologicalDamageContainer
|
||||
- type: Metabolism
|
||||
metabolismHeat: 5000
|
||||
radiatedHeat: 400
|
||||
@@ -305,7 +305,7 @@
|
||||
preset: HumanPreset
|
||||
centerSlot: torso
|
||||
- type: Damageable
|
||||
damagePrototype: biologicalDamageContainer
|
||||
damageContainer: biologicalDamageContainer
|
||||
- type: MobState
|
||||
thresholds:
|
||||
0: !type:NormalMobState {}
|
||||
|
||||
Reference in New Issue
Block a user