using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.GameObjects.Components.Damage; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Shared.Damage.DamageContainer { /// /// Holds the information regarding the various forms of damage an object has /// taken (i.e. brute, burn, or toxic damage). /// [Serializable, NetSerializable] public class DamageContainer { private Dictionary _damageList = DamageTypeExtensions.ToDictionary(); public delegate void HealthChangedDelegate(List changes); [NonSerialized] public readonly HealthChangedDelegate OnHealthChanged; public DamageContainer(HealthChangedDelegate onHealthChanged, DamageContainerPrototype data) { OnHealthChanged = onHealthChanged; SupportedClasses = data.ActiveDamageClasses; } public DamageContainer(HealthChangedDelegate onHealthChanged, List supportedClasses) { OnHealthChanged = onHealthChanged; SupportedClasses = supportedClasses; } public DamageContainer(HealthChangedDelegate onHealthChanged) { OnHealthChanged = onHealthChanged; } [ViewVariables] public virtual List SupportedClasses { get; } [ViewVariables] public virtual List SupportedTypes { get { var toReturn = new List(); foreach (var @class in SupportedClasses) { toReturn.AddRange(@class.ToTypes()); } return toReturn; } } /// /// Sum of all damages kept on record. /// [ViewVariables] public int TotalDamage => _damageList.Values.Sum(); public IReadOnlyDictionary DamageClasses => DamageTypeExtensions.ToClassDictionary(DamageTypes); public IReadOnlyDictionary DamageTypes => _damageList; public bool SupportsDamageClass(DamageClass @class) { return SupportedClasses.Contains(@class); } public bool SupportsDamageType(DamageType type) { return SupportedClasses.Contains(type.ToClass()); } /// /// Attempts to grab the damage value for the given . /// /// /// False if the container does not support that type, true otherwise. /// public bool TryGetDamageValue(DamageType type, [NotNullWhen(true)] out int damage) { return _damageList.TryGetValue(type, out damage); } /// /// Grabs the damage value for the given . /// public int GetDamageValue(DamageType type) { return TryGetDamageValue(type, out var damage) ? damage : 0; } /// /// Attempts to grab the sum of damage values for the given /// . /// /// The class to get the sum for. /// The resulting amount of damage, if any. /// /// True if the class is supported in this container, false otherwise. /// public bool TryGetDamageClassSum(DamageClass @class, [NotNullWhen(true)] out int damage) { damage = 0; if (SupportsDamageClass(@class)) { foreach (var type in @class.ToTypes()) { damage += GetDamageValue(type); } return true; } return false; } /// /// Grabs the sum of damage values for the given . /// public int GetDamageClassSum(DamageClass damageClass) { var sum = 0; foreach (var type in damageClass.ToTypes()) { sum += GetDamageValue(type); } return sum; } /// /// Attempts to change the damage value for the given /// /// /// /// True if successful, false if this container does not support that type. /// public bool TryChangeDamageValue(DamageType type, int delta) { var damageClass = type.ToClass(); if (SupportsDamageClass(damageClass)) { var current = _damageList[type]; current = _damageList[type] = current + delta; if (_damageList[type] < 0) { _damageList[type] = 0; delta = -current; current = 0; } var datum = new HealthChangeData(type, current, delta); var data = new List {datum}; OnHealthChanged(data); return true; } return false; } /// /// Changes the damage value for the given . /// /// The type of damage to change. /// The amount to change it by. /// /// Whether or not to suppress the health change event. /// /// /// True if successful, false if this container does not support that type. /// public bool ChangeDamageValue(DamageType type, int delta, bool quiet = false) { if (!_damageList.TryGetValue(type, out var current)) { return false; } _damageList[type] = current + delta; if (_damageList[type] < 0) { _damageList[type] = 0; delta = -current; } current = _damageList[type]; var datum = new HealthChangeData(type, current, delta); var data = new List {datum}; OnHealthChanged(data); return true; } /// /// Attempts to set the damage value for the given . /// /// /// True if successful, false if this container does not support that type. /// public bool TrySetDamageValue(DamageType type, int newValue) { if (newValue < 0) { return false; } var damageClass = type.ToClass(); if (SupportedClasses.Contains(damageClass)) { var old = _damageList[type] = newValue; _damageList[type] = newValue; var delta = newValue - old; var datum = new HealthChangeData(type, newValue, delta); var data = new List {datum}; OnHealthChanged(data); return true; } return false; } /// /// Tries to set the damage value for the given . /// /// The type of damage to set. /// The value to set it to. /// /// Whether or not to suppress the health changed event. /// /// True if successful, false otherwise. public bool SetDamageValue(DamageType type, int newValue, bool quiet = false) { if (newValue < 0) { return false; } if (!_damageList.ContainsKey(type)) { return false; } var old = _damageList[type]; _damageList[type] = newValue; if (!quiet) { var delta = newValue - old; var datum = new HealthChangeData(type, 0, delta); var data = new List {datum}; OnHealthChanged(data); } return true; } public void Heal() { var data = new List(); foreach (var type in SupportedTypes) { var delta = -GetDamageValue(type); var datum = new HealthChangeData(type, 0, delta); data.Add(datum); SetDamageValue(type, 0, true); } OnHealthChanged(data); } } }