diff --git a/Content.IntegrationTests/Tests/Damageable/DamageTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageTest.cs new file mode 100644 index 0000000000..3cc065aac4 --- /dev/null +++ b/Content.IntegrationTests/Tests/Damageable/DamageTest.cs @@ -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(); + var sMapManager = server.ResolveDependency(); + + 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(); + }); + + await server.WaitRunTicks(5); + + await server.WaitAssertion(() => + { + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); + + var damageToDeal = 7; + + foreach (var type in Enum.GetValues()) + { + 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(); + var sMapManager = server.ResolveDependency(); + + 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(); + }); + + await server.WaitRunTicks(5); + + await server.WaitAssertion(() => + { + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); + + foreach (var @class in Enum.GetValues()) + { + 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); + } + } + }); + } + } +} diff --git a/Content.Shared/Damage/DamageContainer/DamageContainerPrototype.cs b/Content.Shared/Damage/DamageContainer/DamageContainerPrototype.cs index 2138a2520a..ca4a73935c 100644 --- a/Content.Shared/Damage/DamageContainer/DamageContainerPrototype.cs +++ b/Content.Shared/Damage/DamageContainer/DamageContainerPrototype.cs @@ -15,6 +15,7 @@ namespace Content.Shared.Damage.DamageContainer [Serializable, NetSerializable] public class DamageContainerPrototype : IPrototype, IIndexedPrototype { + private bool _supportAll; private HashSet _supportedClasses; private HashSet _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()); serializer.DataField(ref _supportedTypes, "supportedTypes", new HashSet()); - var originalTypes = _supportedTypes.ToArray(); - - foreach (var supportedClass in _supportedClasses) - foreach (var supportedType in supportedClass.ToTypes()) + if (_supportAll) { - _supportedTypes.Add(supportedType); + _supportedClasses.UnionWith(Enum.GetValues()); + _supportedTypes.UnionWith(Enum.GetValues()); + return; } - foreach (var originalType in originalTypes) + foreach (var supportedClass in _supportedClasses) + { + foreach (var supportedType in supportedClass.ToTypes()) + { + _supportedTypes.Add(supportedType); + } + } + + foreach (var originalType in _supportedTypes) { _supportedClasses.Add(originalType.ToClass()); } diff --git a/Content.Shared/Damage/DamageType.cs b/Content.Shared/Damage/DamageType.cs index 810707fcd2..6b139e2569 100644 --- a/Content.Shared/Damage/DamageType.cs +++ b/Content.Shared/Damage/DamageType.cs @@ -58,14 +58,16 @@ 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)) + foreach (var type in @class.ToTypes()) { - continue; - } + if (!types.TryGetValue(type, out var damage)) + { + continue; + } - classes[@class] += damage; + classes[@class] += damage; + } } return classes; diff --git a/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs b/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs index 81fa174b9b..c42f6f7cc9 100644 --- a/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs +++ b/Content.Shared/GameObjects/Components/Damage/DamageableComponent.cs @@ -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; } diff --git a/Content.Shared/GameObjects/Components/Damage/IDamageableComponent.cs b/Content.Shared/GameObjects/Components/Damage/IDamageableComponent.cs index 5e1f3e2f81..21e4cff9e5 100644 --- a/Content.Shared/GameObjects/Components/Damage/IDamageableComponent.cs +++ b/Content.Shared/GameObjects/Components/Damage/IDamageableComponent.cs @@ -70,6 +70,16 @@ namespace Content.Shared.GameObjects.Components.Damage /// bool TryGetDamage(DamageType type, out int damage); + /// + /// Gets the amount of damage of a class. + /// + /// The class to get the damage of. + /// The amount of damage of that class. + /// + /// True if the given is supported, false otherwise. + /// + bool TryGetDamage(DamageClass @class, out int damage); + /// /// Changes the specified , applying /// resistance values only if it is damage. diff --git a/Resources/Prototypes/Damage/damage_containers.yml b/Resources/Prototypes/Damage/damage_containers.yml index ca55e0fb95..e44898a109 100644 --- a/Resources/Prototypes/Damage/damage_containers.yml +++ b/Resources/Prototypes/Damage/damage_containers.yml @@ -1,3 +1,7 @@ +- type: damageContainer + id: allDamageContainer + supportAll: true + - type: damageContainer id: biologicalDamageContainer supportedClasses: diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 64af99abc8..e7920cd0a1 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -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 {}