Revert "Refactor Damage to use Protoypes (#4262)"

This reverts commit 20bf5739a9.
This commit is contained in:
Silver
2021-08-24 00:50:39 -06:00
committed by Silver
parent 20bf5739a9
commit e708091518
121 changed files with 711 additions and 10237 deletions

View File

@@ -18,242 +18,67 @@ namespace Content.Shared.Damage.Components
{
/// <summary>
/// Component that allows attached entities to take damage.
/// This basic version never dies (thus can take an indefinite amount of damage).
/// </summary>
/// <remarks>
/// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>s. DamageContainers
/// are effectively a dictionary of damage types and damage numbers, along with functions to modify them. Damage
/// groups are collections of damage types. A damage group is 'applicable' to a damageable component if it
/// supports at least one damage type in that group. A subset of these groups may be 'fully supported' when every
/// member of the group is supported by the container. This basic version never dies (thus can take an
/// indefinite amount of damage).
/// </remarks>
[RegisterComponent]
[ComponentReference(typeof(IDamageableComponent))]
[NetworkedComponent()]
public class DamageableComponent : Component, IDamageableComponent, IRadiationAct, ISerializationHooks
{
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
=======
=======
>>>>>>> fix a few bugs
public override string Name => "Damageable";
public override uint? NetID => ContentNetIDs.DAMAGEABLE;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
>>>>>>> update damagecomponent across shared and server
=======
>>>>>>> Fix Merge issues
// TODO define these in yaml?
public const string DefaultResistanceSet = "defaultResistances";
public const string DefaultDamageContainer = "metallicDamageContainer";
=======
/// <summary>
/// The main damage dictionary. All the damage information is stored in this dictionary with <see cref="DamageTypePrototype"/> keys.
/// </summary>
private Dictionary<DamageTypePrototype, int> _damageDict = new();
>>>>>>> Refactor damageablecomponent update (#4406)
<<<<<<< refs/remotes/origin/master
private readonly Dictionary<DamageType, int> _damageList = DamageTypeExtensions.ToNewDictionary();
[DataField("resistances")] public string ResistanceSetId = DefaultResistanceSet;
=======
[DataField("resistances")]
public string ResistanceSetId { get; set; } = "defaultResistances";
[ViewVariables] public ResistanceSet Resistances { get; set; } = new();
>>>>>>> Merge fixes
// TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization
<<<<<<< refs/remotes/origin/master
[ViewVariables] [DataField("damageContainer")] public string DamageContainerId { get; set; } = DefaultDamageContainer;
=======
/// <summary>
/// The main damage dictionary. All the damage information is stored in this dictionary with <see cref="DamageTypePrototype"/> keys.
/// </summary>
private Dictionary<DamageTypePrototype, int> _damageDict = new();
[DataField("resistances")]
public string ResistanceSetId { get; set; } = "defaultResistances";
>>>>>>> refactor-damageablecomponent
[ViewVariables] public ResistanceSet Resistances { get; set; } = new();
=======
[ViewVariables]
[DataField("damageContainer")]
<<<<<<< refs/remotes/origin/master
public string DamageContainerId { get; set; } = DefaultDamageContainer;
>>>>>>> fix a few bugs
// TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization
[ViewVariables]
[DataField("damageContainer")]
public string DamageContainerId { get; set; } = "metallicDamageContainer";
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
[ViewVariables] public IReadOnlyDictionary<DamageClass, int> DamageClasses => _damageList.ToClassDictionary();
=======
// TODO DAMAGE Cache this
// When moving logic from damageableComponent --> Damage System, make damageSystem update these on damage change.
[ViewVariables] public int TotalDamage => _damageDict.Values.Sum();
[ViewVariables] public IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType => _damageDict;
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup => DamageTypeDictToDamageGroupDict(_damageDict, ApplicableDamageGroups);
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup => DamageTypeDictToDamageGroupDict(_damageDict, FullySupportedDamageGroups);
>>>>>>> refactor-damageablecomponent
[ViewVariables] public int TotalDamage => _damageList.Values.Sum();
// Whenever sending over network, also need a <string, int> dictionary
// TODO DAMAGE MAYBE Cache this?
public IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs => ConvertDictKeysToIDs(GetDamagePerApplicableGroup);
public IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs => ConvertDictKeysToIDs(GetDamagePerFullySupportedGroup);
public IReadOnlyDictionary<string, int> GetDamagePerTypeIDs => ConvertDictKeysToIDs(_damageDict);
[ViewVariables] public IReadOnlyDictionary<DamageClass, int> DamageClasses => _damageList.ToClassDictionary();
// TODO PROTOTYPE Replace these datafield variables with prototype references, once they are supported.
// Also requires appropriate changes in OnExplosion() and RadiationAct()
[ViewVariables]
[DataField("radiationDamageTypes")]
public List<string> RadiationDamageTypeIDs { get; set; } = new() {"Radiation"};
[ViewVariables]
[DataField("explosionDamageTypes")]
public List<string> ExplosionDamageTypeIDs { get; set; } = new() { "Piercing", "Heat" };
[ViewVariables] public IReadOnlyDictionary<DamageType, int> DamageTypes => _damageList;
public HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; } = new();
[ViewVariables] public HashSet<DamageType> SupportedTypes { get; } = new();
[ViewVariables] public HashSet<DamageClass> SupportedClasses { get; } = new();
<<<<<<< HEAD
public bool SupportsDamageClass(DamageClass @class)
{
return SupportedClasses.Contains(@class);
=======
public HashSet<DamageGroupPrototype> SupportedGroups { get; } = new();
=======
public string DamageContainerId { get; set; } = "metallicDamageContainer";
// TODO DAMAGE Cache this
// When moving logic from damageableComponent --> Damage System, make damageSystem update these on damage change.
[ViewVariables] public int TotalDamage => _damageDict.Values.Sum();
[ViewVariables] public IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType => _damageDict;
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup => DamageTypeDictToDamageGroupDict(_damageDict, ApplicableDamageGroups);
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup => DamageTypeDictToDamageGroupDict(_damageDict, FullySupportedDamageGroups);
// Whenever sending over network, also need a <string, int> dictionary
// TODO DAMAGE MAYBE Cache this?
public IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs => ConvertDictKeysToIDs(GetDamagePerApplicableGroup);
public IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs => ConvertDictKeysToIDs(GetDamagePerFullySupportedGroup);
public IReadOnlyDictionary<string, int> GetDamagePerTypeIDs => ConvertDictKeysToIDs(_damageDict);
// TODO PROTOTYPE Replace these datafield variables with prototype references, once they are supported.
// Also requires appropriate changes in OnExplosion() and RadiationAct()
[ViewVariables]
[DataField("radiationDamageTypes")]
public List<string> RadiationDamageTypeIDs { get; set; } = new() {"Radiation"};
[ViewVariables]
[DataField("explosionDamageTypes")]
public List<string> ExplosionDamageTypeIDs { get; set; } = new() { "Piercing", "Heat" };
public HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; } = new();
>>>>>>> Refactor damageablecomponent update (#4406)
public HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; } = new();
public HashSet<DamageTypePrototype> SupportedDamageTypes { get; } = new();
<<<<<<< refs/remotes/origin/master
public bool SupportsDamageClass(DamageGroupPrototype damageGroup)
{
return SupportedGroups.Contains(damageGroup);
>>>>>>> Merge fixes
}
=======
public HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; } = new();
>>>>>>> refactor-damageablecomponent
public HashSet<DamageTypePrototype> SupportedDamageTypes { get; } = new();
public bool SupportsDamageType(DamageType type)
{
return SupportedTypes.Contains(type);
}
=======
>>>>>>> update damagecomponent across shared and server
protected override void Initialize()
{
base.Initialize();
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
=======
>>>>>>> refactor-damageablecomponent
// TODO DAMAGE Serialize damage done and resistance changes
var damageContainerPrototype = _prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
var damagePrototype = prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
ApplicableDamageGroups.Clear();
FullySupportedDamageGroups.Clear();
SupportedDamageTypes.Clear();
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
SupportedClasses.Clear();
SupportedTypes.Clear();
=======
=======
>>>>>>> fix a few bugs
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
=======
>>>>>>> Refactor damageablecomponent update (#4406)
// TODO DAMAGE Serialize damage done and resistance changes
var damageContainerPrototype = _prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
>>>>>>> update damagecomponent across shared and server
<<<<<<< refs/remotes/origin/master
DamageContainerId = damagePrototype.ID;
SupportedClasses.UnionWith(damagePrototype.SupportedClasses);
SupportedTypes.UnionWith(damagePrototype.SupportedTypes);
=======
SupportedGroups.Clear();
SupportedTypes.Clear();
=======
ApplicableDamageGroups.Clear();
FullySupportedDamageGroups.Clear();
SupportedDamageTypes.Clear();
>>>>>>> Refactor damageablecomponent update (#4406)
//Get Damage groups/types from the DamageContainerPrototype.
DamageContainerId = damageContainerPrototype.ID;
<<<<<<< refs/remotes/origin/master
SupportedGroups.UnionWith(damageContainerPrototype.SupportedDamageGroups);
SupportedTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
>>>>>>> Merge fixes
=======
ApplicableDamageGroups.UnionWith(damageContainerPrototype.ApplicableDamageGroups);
FullySupportedDamageGroups.UnionWith(damageContainerPrototype.FullySupportedDamageGroups);
SupportedDamageTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
>>>>>>> Refactor damageablecomponent update (#4406)
=======
//Get Damage groups/types from the DamageContainerPrototype.
DamageContainerId = damageContainerPrototype.ID;
ApplicableDamageGroups.UnionWith(damageContainerPrototype.ApplicableDamageGroups);
FullySupportedDamageGroups.UnionWith(damageContainerPrototype.FullySupportedDamageGroups);
SupportedDamageTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
>>>>>>> refactor-damageablecomponent
//initialize damage dictionary 0 damage
_damageDict = new(SupportedDamageTypes.Count);
foreach (var type in SupportedDamageTypes)
{
_damageDict.Add(type, 0);
}
Resistances = new ResistanceSet(_prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId));
var resistancePrototype = prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId);
Resistances = new ResistanceSet(resistancePrototype);
}
protected override void Startup()
@@ -263,25 +88,9 @@ namespace Content.Shared.Damage.Components
ForceHealthChangedEvent();
}
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
=======
public DamageTypePrototype GetDamageType(string ID)
{
return _prototypeManager.Index<DamageTypePrototype>(ID);
}
public DamageGroupPrototype GetDamageGroup(string ID)
{
return _prototypeManager.Index<DamageGroupPrototype>(ID);
}
>>>>>>> update damagecomponent across shared and server
=======
>>>>>>> Refactor damageablecomponent update (#4406)
public override ComponentState GetComponentState(ICommonSession player)
{
return new DamageableComponentState(GetDamagePerTypeIDs);
return new DamageableComponentState(_damageList);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
@@ -293,104 +102,49 @@ namespace Content.Shared.Damage.Components
return;
}
_damageDict.Clear();
_damageList.Clear();
foreach (var (type, damage) in state.DamageDict)
foreach (var (type, damage) in state.DamageList)
{
_damageDict[_prototypeManager.Index<DamageTypePrototype>(type)] = damage;
_damageList[type] = damage;
}
}
public int GetDamage(DamageTypePrototype type)
public int GetDamage(DamageType type)
{
return GetDamagePerType.GetValueOrDefault(type);
return _damageList.GetValueOrDefault(type);
}
public bool TryGetDamage(DamageTypePrototype type, out int damage)
public bool TryGetDamage(DamageType type, out int damage)
{
return GetDamagePerType.TryGetValue(type, out damage);
return _damageList.TryGetValue(type, out damage);
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
public int GetDamage(DamageClass @class)
{
if (!SupportsDamageClass(@class))
{
return 0;
}
=======
public int GetDamage(DamageGroupPrototype group)
{
return GetDamagePerApplicableGroup.GetValueOrDefault(group);
}
>>>>>>> Refactor damageablecomponent update (#4406)
=======
public int GetDamage(DamageGroupPrototype group)
{
return GetDamagePerApplicableGroup.GetValueOrDefault(group);
}
>>>>>>> refactor-damageablecomponent
public bool TryGetDamage(DamageGroupPrototype group, out int damage)
{
return GetDamagePerApplicableGroup.TryGetValue(group, out damage);
}
var damage = 0;
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
foreach (var type in @class.ToTypes())
{
damage += GetDamage(type);
}
=======
=======
>>>>>>> refactor-damageablecomponent
public bool IsApplicableDamageGroup(DamageGroupPrototype group)
{
return ApplicableDamageGroups.Contains(group);
return damage;
}
public bool IsFullySupportedDamageGroup(DamageGroupPrototype group)
{
return FullySupportedDamageGroups.Contains(group);
<<<<<<< HEAD
=======
}
public bool IsSupportedDamageType(DamageTypePrototype type)
{
return SupportedDamageTypes.Contains(type);
>>>>>>> refactor-damageablecomponent
}
>>>>>>> Refactor damageablecomponent update (#4406)
<<<<<<< HEAD
public bool IsSupportedDamageType(DamageTypePrototype type)
{
return SupportedDamageTypes.Contains(type);
}
<<<<<<< refs/remotes/origin/master
public bool TryGetDamage(DamageClass @class, out int damage)
{
if (!SupportsDamageClass(@class))
=======
public bool TrySetDamage(DamageGroupPrototype group, int newValue)
{
if (!ApplicableDamageGroups.Contains(group))
>>>>>>> Refactor damageablecomponent update (#4406)
=======
public bool TrySetDamage(DamageGroupPrototype group, int newValue)
{
if (!ApplicableDamageGroups.Contains(group))
>>>>>>> refactor-damageablecomponent
{
damage = 0;
return false;
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
damage = GetDamage(@class);
return true;
}
@@ -402,90 +156,44 @@ namespace Content.Shared.Damage.Components
/// True if successful, false if this container does not support that type.
/// </returns>
public bool TrySetDamage(DamageType type, int newValue)
=======
if (newValue < 0)
{
// invalid value
return false;
}
foreach (var type in group.DamageTypes)
{
TrySetDamage(type, newValue);
}
return true;
}
public bool TrySetAllDamage(int newValue)
>>>>>>> Refactor damageablecomponent update (#4406)
=======
if (newValue < 0)
{
// invalid value
return false;
}
foreach (var type in group.DamageTypes)
{
TrySetDamage(type, newValue);
}
return true;
}
public bool TrySetAllDamage(int newValue)
>>>>>>> refactor-damageablecomponent
{
if (newValue < 0)
{
// invalid value
return false;
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
var damageClass = type.ToClass();
if (SupportedClasses.Contains(damageClass))
=======
if (SupportedTypes.Contains(type))
>>>>>>> Merge fixes
=======
foreach (var type in SupportedDamageTypes)
>>>>>>> Refactor damageablecomponent update (#4406)
=======
foreach (var type in SupportedDamageTypes)
>>>>>>> refactor-damageablecomponent
{
TrySetDamage(type, newValue);
var old = _damageList[type] = newValue;
_damageList[type] = newValue;
var delta = newValue - old;
var datum = new DamageChangeData(type, newValue, delta);
var data = new List<DamageChangeData> {datum};
OnHealthChanged(data);
return true;
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
return false;
}
public void Heal(DamageType type)
{
SetDamage(type, 0);
=======
return true;
>>>>>>> Refactor damageablecomponent update (#4406)
=======
return true;
>>>>>>> refactor-damageablecomponent
}
public bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false)
public void Heal()
{
// Check if damage type is supported, and get the current value if it is.
if (!GetDamagePerType.TryGetValue(type, out var current))
foreach (var type in SupportedTypes)
{
return false;
Heal(type);
}
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
public bool ChangeDamage(
DamageType type,
int amount,
@@ -494,19 +202,13 @@ namespace Content.Shared.Damage.Components
DamageChangeParams? extraParams = null)
{
if (!SupportsDamageType(type))
=======
if (amount == 0)
>>>>>>> Refactor damageablecomponent update (#4406)
=======
if (amount == 0)
>>>>>>> refactor-damageablecomponent
{
return false;
}
// Apply resistances (does nothing if amount<0)
var finalDamage = amount;
if (!ignoreDamageResistances)
if (!ignoreResistances)
{
finalDamage = Resistances.CalculateDamage(type, amount);
}
@@ -514,23 +216,24 @@ namespace Content.Shared.Damage.Components
if (finalDamage == 0)
return false;
// Are we healing below zero?
if (!_damageList.TryGetValue(type, out var current))
{
return false;
}
if (current + finalDamage < 0)
{
if (current == 0)
// Damage type is supported, but there is nothing to do
return false;
// Cap healing down to zero
_damageDict[type] = 0;
_damageList[type] = 0;
finalDamage = -current;
}
else
{
_damageDict[type] = current + finalDamage;
_damageList[type] = current + finalDamage;
}
current = _damageDict[type];
current = _damageList[type];
var datum = new DamageChangeData(type, current, finalDamage);
var data = new List<DamageChangeData> {datum};
@@ -540,8 +243,6 @@ namespace Content.Shared.Damage.Components
return true;
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
public bool ChangeDamage(DamageClass @class, int amount, bool ignoreResistances,
IEntity? source = null,
DamageChangeParams? extraParams = null)
@@ -551,149 +252,98 @@ namespace Content.Shared.Damage.Components
return false;
}
<<<<<<< refs/remotes/origin/master
var types = @class.ToTypes();
=======
var types = damageGroup.DamageTypes.ToArray();
>>>>>>> Merge fixes
=======
public bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false)
{
var types = group.DamageTypes.ToArray();
>>>>>>> Refactor damageablecomponent update (#4406)
=======
public bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false)
{
var types = group.DamageTypes.ToArray();
>>>>>>> refactor-damageablecomponent
if (amount < 0)
{
// We are Healing. Keep track of how much we can hand out (with a better var name for readability).
var availableHealing = -amount;
// 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 healThisCycle = 1;
// Get total group damage.
var damageToHeal = GetDamagePerApplicableGroup[group];
<<<<<<< HEAD
// Is there any damage to even heal?
if (damageToHeal == 0)
return false;
=======
// Is there any damage to even heal?
if (damageToHeal == 0)
return false;
>>>>>>> refactor-damageablecomponent
// If total healing is more than there is damage, just set to 0 and return.
if (damageToHeal <= availableHealing)
// While we have healing left...
while (healingLeft > 0 && healThisCycle != 0)
{
TrySetDamage(group, 0);
return true;
}
// Infinite loop fallback, if no healing was done in a cycle
// then exit
healThisCycle = 0;
// Partially heal each damage group
int healing, damage;
foreach (var type in types)
{
if (!_damageDict.TryGetValue(type, out damage))
int healPerType;
if (healingLeft < types.Count)
{
// Damage Type is not supported. Continue without reducing availableHealing
continue;
// Say we were to distribute 2 healing between 3
// this will distribute 1 to each (and stop after 2 are given)
healPerType = 1;
}
else
{
// Say we were to distribute 62 healing between 3
// this will distribute 20 to each, leaving 2 for next loop
healPerType = healingLeft / types.Count;
}
// Apply healing to the damage type. The healing amount may be zero if either damage==0, or if
// integer rounding made it zero (i.e., damage is small)
healing = (availableHealing * damage) / damageToHeal;
TryChangeDamage(type, -healing, ignoreDamageResistances);
// remove this damage type from the damage we consider for future loops, regardless of how much we
// actually healed this type.
damageToHeal -= damage;
availableHealing -= healing;
// If we now healed all the damage, exit. otherwise 1/0 and universe explodes.
if (damageToHeal == 0)
foreach (var type in types)
{
break;
var damage = GetDamage(type);
var healAmount = Math.Min(healingLeft, damage);
healAmount = Math.Min(healAmount, healPerType);
ChangeDamage(type, -healAmount, true);
healThisCycle += healAmount;
healingLeft -= healAmount;
}
}
// Damage type is supported, there was damage to heal, and resistances were ignored
// --> Damage must have changed
return true;
}
else if (amount > 0)
var damageLeft = amount;
while (damageLeft > 0)
{
// Resistances may result in no actual damage change. We need to keep track if any damage got through.
var damageChanged = false;
int damagePerType;
// We are adding damage. Keep track of how much we can dish out (with a better var name for readability).
var availableDamage = amount;
if (damageLeft < types.Count)
{
damagePerType = 1;
}
else
{
damagePerType = damageLeft / types.Count;
}
// How many damage types do we have to distribute over?.
var numberDamageTypes = types.Length;
// Apply damage to each damage group
int damage;
foreach (var type in types)
{
// Distribute the remaining damage over the remaining damage types.
damage = availableDamage / numberDamageTypes;
// Try apply the damage type. If damage type is not supported, this has no effect.
// We also use the return value to check whether any damage has changed
damageChanged = TryChangeDamage(type, damage, ignoreDamageResistances) || damageChanged;
// regardless of whether we dealt damage, reduce the amount to distribute.
availableDamage -= damage;
numberDamageTypes -= 1;
var damageAmount = Math.Min(damagePerType, damageLeft);
ChangeDamage(type, damageAmount, true);
damageLeft -= damageAmount;
}
return damageChanged;
}
// amount==0 no damage change.
return false;
return true;
}
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
public bool SetDamage(DamageType type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
=======
public bool SetDamage(DamageTypePrototype type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
>>>>>>> Fix Merge issues
=======
public bool TrySetDamage(DamageTypePrototype type, int newValue)
>>>>>>> Refactor damageablecomponent update (#4406)
=======
public bool TrySetDamage(DamageTypePrototype type, int newValue)
>>>>>>> refactor-damageablecomponent
{
if (!_damageDict.TryGetValue(type, out var oldValue))
if (newValue >= TotalDamage)
{
return false;
}
if (newValue < 0)
{
// invalid value
return false;
}
if (oldValue == newValue)
if (!_damageList.ContainsKey(type))
{
// No health change.
// But we are trying to set, not trying to change.
return true;
return false;
}
_damageDict[type] = newValue;
var old = _damageList[type];
_damageList[type] = newValue;
var delta = newValue - oldValue;
var delta = newValue - old;
var datum = new DamageChangeData(type, 0, delta);
var data = new List<DamageChangeData> {datum};
@@ -706,7 +356,7 @@ namespace Content.Shared.Damage.Components
{
var data = new List<DamageChangeData>();
foreach (var type in SupportedDamageTypes)
foreach (var type in SupportedTypes)
{
var damage = GetDamage(type);
var datum = new DamageChangeData(type, damage, 0);
@@ -722,32 +372,6 @@ namespace Content.Shared.Damage.Components
OnHealthChanged(args);
}
<<<<<<< refs/remotes/origin/master
<<<<<<< refs/remotes/origin/master
=======
private IReadOnlyDictionary<DamageGroupPrototype, int> damageListToDamageGroup(IReadOnlyDictionary<DamageTypePrototype, int> damagelist)
{
var damageGroupDict = new Dictionary<DamageGroupPrototype, int>();
int damageGroupSumDamage = 0;
int damageTypeDamage = 0;
foreach (var damageGroup in SupportedGroups)
{
damageGroupSumDamage = 0;
foreach (var damageType in SupportedTypes)
{
damageTypeDamage = 0;
damagelist.TryGetValue(damageType, out damageTypeDamage);
damageGroupSumDamage += damageTypeDamage;
}
damageGroupDict.Add(damageGroup, damageGroupSumDamage);
}
return damageGroupDict;
}
>>>>>>> Merge fixes
=======
>>>>>>> Refactor damageablecomponent update (#4406)
protected virtual void OnHealthChanged(DamageChangedEventArgs e)
{
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, e);
@@ -758,25 +382,11 @@ namespace Content.Shared.Damage.Components
Dirty();
}
public void RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
{
var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1);
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
ChangeDamage(DamageType.Radiation, totalDamage, false, radiation.Owner);
=======
=======
>>>>>>> refactor-damageablecomponent
foreach (var typeID in RadiationDamageTypeIDs)
{
TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(typeID), totalDamage);
}
<<<<<<< HEAD
>>>>>>> Refactor damageablecomponent update (#4406)
=======
>>>>>>> refactor-damageablecomponent
}
public void OnExplosion(ExplosionEventArgs eventArgs)
@@ -789,99 +399,19 @@ namespace Content.Shared.Damage.Components
_ => throw new ArgumentOutOfRangeException()
};
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
ChangeDamage(DamageType.Piercing, damage, false);
ChangeDamage(DamageType.Heat, damage, false);
=======
=======
>>>>>>> refactor-damageablecomponent
foreach (var typeID in ExplosionDamageTypeIDs)
{
TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(typeID), damage);
}
}
/// <summary>
/// Take a dictionary with <see cref="IPrototype"/> keys and return a dictionary using <see cref="IPrototype.ID"/> as keys
/// instead.
/// </summary>
/// <remarks>
/// Useful when sending damage type and group prototypes dictionaries over the network.
/// </remarks>
public static IReadOnlyDictionary<string, int>
ConvertDictKeysToIDs<TPrototype>(IReadOnlyDictionary<TPrototype, int> prototypeDict)
where TPrototype : IPrototype
{
Dictionary<string, int> idDict = new(prototypeDict.Count);
foreach (var entry in prototypeDict)
{
idDict.Add(entry.Key.ID, entry.Value);
}
return idDict;
<<<<<<< HEAD
>>>>>>> Refactor damageablecomponent update (#4406)
=======
>>>>>>> refactor-damageablecomponent
}
/// <summary>
/// Convert a dictionary with damage type keys to a dictionary of damage groups keys.
/// </summary>
/// <remarks>
/// Takes a dictionary with damage types as keys and integers as values, and an iterable list of damage
/// groups. Returns a dictionary with damage group keys, with values calculated by adding up the values for
/// each damage type in that group. If a damage type is associated with more than one supported damage
/// group, it will contribute to the total of each group. Conversely, some damage types may not contribute
/// to the new dictionary if their associated group(s) are not in given list of groups.
/// </remarks>
public static IReadOnlyDictionary<DamageGroupPrototype, int>
DamageTypeDictToDamageGroupDict(IReadOnlyDictionary<DamageTypePrototype, int> damageTypeDict, IEnumerable<DamageGroupPrototype> groupKeys)
{
var damageGroupDict = new Dictionary<DamageGroupPrototype, int>();
int damageGroupSumDamage, damageTypeDamage;
// iterate over the list of group keys for our new dictionary
foreach (var group in groupKeys)
{
// For each damage type in this group, add up the damage present in the given dictionary
damageGroupSumDamage = 0;
foreach (var type in group.DamageTypes)
{
// if the damage type is in the dictionary, add it's damage to the group total.
if (damageTypeDict.TryGetValue(type, out damageTypeDamage))
{
damageGroupSumDamage += damageTypeDamage;
}
}
damageGroupDict.Add(group, damageGroupSumDamage);
}
return damageGroupDict;
}
}
[Serializable, NetSerializable]
public class DamageableComponentState : ComponentState
{
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
public readonly Dictionary<DamageType, int> DamageList;
public DamageableComponentState(Dictionary<DamageType, int> damageList)
=======
public readonly IReadOnlyDictionary<string, int> DamageDict;
public DamageableComponentState(IReadOnlyDictionary<string, int> damageDict)
>>>>>>> Refactor damageablecomponent update (#4406)
=======
public readonly IReadOnlyDictionary<string, int> DamageDict;
public DamageableComponentState(IReadOnlyDictionary<string, int> damageDict)
>>>>>>> refactor-damageablecomponent
{
DamageDict = damageDict;
DamageList = damageList;
}
}
}

View File

@@ -8,212 +8,75 @@ namespace Content.Shared.Damage.Components
public interface IDamageableComponent : IComponent, IExAct
{
/// <summary>
/// The sum of all damages types in the DamageableComponent.
/// Sum of all damages taken.
/// </summary>
int TotalDamage { get; }
/// <summary>
/// Returns a dictionary of the damage in the container, indexed by applicable <see cref="DamageGroupPrototype"/>.
/// The amount of damage mapped by <see cref="DamageClass"/>.
/// </summary>
/// <remarks>
/// The values represent the sum of all damage in each group. If a supported damage type is a member of more than one group, it will contribute to each one.
/// Therefore, the sum of the values may be greater than the sum of the values in the dictionary returned by <see cref="GetDamagePerType"/>
/// </remarks>
IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup { get; }
IReadOnlyDictionary<DamageClass, int> DamageClasses { get; }
/// <summary>
/// Returns a dictionary of the damage in the container, indexed by fully supported instances of <see
/// cref="DamageGroupPrototype"/>.
/// The amount of damage mapped by <see cref="DamageType"/>.
/// </summary>
/// <remarks>
/// The values represent the sum of all damage in each group. As the damage container may have some damage
/// types that are not part of a fully supported damage group, the sum of the values may be less of the values
/// in the dictionary returned by <see cref="GetDamagePerType"/>. On the other hand, if a supported damage type
/// is a member of more than one group, it will contribute to each one. Therefore, the sum may also be greater
/// instead.
/// </remarks>
IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup { get; }
IReadOnlyDictionary<DamageType, int> DamageTypes { get; }
/// <summary>
/// Returns a dictionary of the damage in the container, indexed by <see cref="DamageTypePrototype"/>.
/// </summary>
IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType { get; }
HashSet<DamageType> SupportedTypes { get; }
/// <summary>
/// Like <see cref="GetDamagePerApplicableGroup"/>, but indexed by <see cref="DamageGroupPrototype.ID"/>
/// </summary>
IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs { get; }
HashSet<DamageClass> SupportedClasses { get; }
/// <summary>
/// Like <see cref="GetDamagePerFullySupportedGroup"/>, but indexed by <see cref="DamageGroupPrototype.ID"/>
/// </summary>
IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs { get; }
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
bool SupportsDamageClass(DamageClass @class);
bool SupportsDamageType(DamageType type);
=======
/// <summary>
/// Like <see cref="GetDamagePerType"/>, but indexed by <see cref="DamageTypePrototype.ID"/>
/// </summary>
IReadOnlyDictionary<string, int> GetDamagePerTypeIDs { get; }
/// <summary>
/// Collection of damage types supported by this DamageableComponent.
/// </summary>
/// <remarks>
/// Each of these damage types is fully supported. If any of these damage types is a
/// member of a damage group, these groups are represented in <see cref="ApplicableDamageGroups"></see>
/// </remarks>
HashSet<DamageTypePrototype> SupportedDamageTypes { get; }
/// <summary>
/// Collection of damage groups that are fully supported by DamageableComponent.
/// </summary>
/// <remarks>
/// This describes what damage groups this damage container explicitly supports. It supports every damage type
/// contained in these damage groups. It may also support other damage types not in these groups. To see all
/// damage types <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
/// cref="ApplicableDamageGroups"/>.
/// </remarks>
HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; }
>>>>>>> Refactor damageablecomponent update (#4406)
/// <summary>
/// Collection of damage groups that could apply damage to this DamageableComponent.
/// </summary>
/// <remarks>
/// This describes what damage groups could have an effect on this damage container. However not every damage
/// group has to be fully supported. For example, the container may support ONLY the piercing damage type. It should
/// therefore be affected by instances of brute damage, but does not necessarily support blunt or slash damage.
/// For a list of supported damage types, see <see cref="SupportedDamageTypes"/>.
/// </remarks>
HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; }
=======
/// <summary>
/// Like <see cref="GetDamagePerType"/>, but indexed by <see cref="DamageTypePrototype.ID"/>
/// </summary>
IReadOnlyDictionary<string, int> GetDamagePerTypeIDs { get; }
/// <summary>
/// Collection of damage types supported by this DamageableComponent.
/// </summary>
/// <remarks>
/// Each of these damage types is fully supported. If any of these damage types is a
/// member of a damage group, these groups are represented in <see cref="ApplicableDamageGroups"></see>
/// </remarks>
HashSet<DamageTypePrototype> SupportedDamageTypes { get; }
/// <summary>
/// Collection of damage groups that are fully supported by DamageableComponent.
/// </summary>
/// <remarks>
/// This describes what damage groups this damage container explicitly supports. It supports every damage type
/// contained in these damage groups. It may also support other damage types not in these groups. To see all
/// damage types <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
/// cref="ApplicableDamageGroups"/>.
/// </remarks>
HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; }
/// <summary>
/// Collection of damage groups that could apply damage to this DamageableComponent.
/// </summary>
/// <remarks>
/// This describes what damage groups could have an effect on this damage container. However not every damage
/// group has to be fully supported. For example, the container may support ONLY the piercing damage type. It should
/// therefore be affected by instances of brute damage, but does not necessarily support blunt or slash damage.
/// For a list of supported damage types, see <see cref="SupportedDamageTypes"/>.
/// </remarks>
HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; }
>>>>>>> refactor-damageablecomponent
/// <summary>
/// The resistances of this component.
/// </summary>
ResistanceSet Resistances { get; }
bool SupportsDamageClass(DamageClass @class);
bool SupportsDamageType(DamageType type);
/// <summary>
/// Tries to get the amount of damage of a type.
/// Gets the amount of damage of a type.
/// </summary>
/// <param name="type">The type to get the damage of.</param>
/// <param name="damage">The amount of damage of that type.</param>
/// <returns>
/// True if the given <see cref="type"/> is supported, false otherwise.
/// </returns>
bool TryGetDamage(DamageTypePrototype type, out int damage);
bool TryGetDamage(DamageType type, out int damage);
/// <summary>
/// Returns the amount of damage of a given type, or zero if it is not supported.
/// Gets the amount of damage of a class.
/// </summary>
int GetDamage(DamageTypePrototype type);
/// <summary>
<<<<<<< HEAD
/// Returns the amount of damage of a given type, or zero if it is not supported.
/// </summary>
int GetDamage(DamageTypePrototype type);
/// <summary>
/// Tries to get the total amount of damage in a damage group.
/// </summary>
=======
/// Tries to get the total amount of damage in a damage group.
/// </summary>
>>>>>>> refactor-damageablecomponent
/// <param name="group">The group to get the damage of.</param>
/// <param name="damage">The amount of damage in that group.</param>
/// <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 group is applicable to this container, false otherwise.
/// True if the given <see cref="@class"/> is supported, false otherwise.
/// </returns>
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
bool TryGetDamage(DamageClass @class, out int damage);
/// <summary>
/// Changes the specified <see cref="DamageType"/>, applying
/// resistance values only if it is damage.
=======
bool TryGetDamage(DamageGroupPrototype group, out int damage);
/// <summary>
/// Returns the amount of damage present in an applicable group, or zero if no members are supported.
/// </summary>
int GetDamage(DamageGroupPrototype group);
/// <summary>
/// Tries to change the specified <see cref="DamageTypePrototype"/>, applying
/// resistance values only if it is dealing damage.
>>>>>>> Refactor damageablecomponent update (#4406)
=======
bool TryGetDamage(DamageGroupPrototype group, out int damage);
/// <summary>
/// Returns the amount of damage present in an applicable group, or zero if no members are supported.
/// </summary>
int GetDamage(DamageGroupPrototype group);
/// <summary>
/// Tries to change the specified <see cref="DamageTypePrototype"/>, applying
/// resistance values only if it is dealing damage.
>>>>>>> refactor-damageablecomponent
/// </summary>
/// <param name="type">Type of damage being changed.</param>
/// <param name="amount">
/// Amount of damage being received (positive for damage, negative for heals).
/// </param>
/// <param name="ignoreDamageResistances">
/// Whether or not to ignore resistances when taking damage.
/// <param name="ignoreResistances">
/// Whether or not to ignore resistances.
/// Healing always ignores resistances, regardless of this input.
/// </param>
/// <param name="source">
/// The entity that dealt or healed the damage, if any.
/// </param>
/// <param name="extraParams">
/// Extra parameters that some components may require, such as a specific limb to target.
/// </param>
/// <returns>
/// False if the given type is not supported or no damage change occurred; true otherwise.
/// False if the given type is not supported or improper
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
/// </returns>
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
bool ChangeDamage(
DamageType type,
int amount,
@@ -226,47 +89,24 @@ namespace Content.Shared.Damage.Components
/// resistance values only if it is damage.
/// Spreads amount evenly between the <see cref="DamageType"></see>s
/// represented by that class.
=======
bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false);
/// <summary>
/// Tries to change damage of the specified <see cref="DamageGroupPrototype"/>, applying resistance values
/// only if it is damage.
>>>>>>> Refactor damageablecomponent update (#4406)
=======
bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false);
/// <summary>
/// Tries to change damage of the specified <see cref="DamageGroupPrototype"/>, applying resistance values
/// only if it is damage.
>>>>>>> refactor-damageablecomponent
/// </summary>
/// <remarks>
/// <para>
/// If dealing damage, this spreads the damage change amount evenly between the <see
/// cref="DamageTypePrototype"></see>s in this group (subject to integer rounding). If only a subset of the
/// damage types in the group are actually supported, then the total damage dealt may be less than expected
/// (unsupported damage is ignored).
/// </para>
/// <para>
/// If healing damage, this spreads the damage change proportional to the current damage value of each <see
/// cref="DamageTypePrototype"></see> (subject to integer rounding). If there is less damage than is being
/// healed, some healing is wasted. Unsupported damage types do not waste healing.
/// </para>
/// </remarks>
/// <param name="group">group of damage being changed.</param>
/// <param name="class">Class of damage being changed.</param>
/// <param name="amount">
/// Amount of damage being received (positive for damage, negative for heals).
/// </param>
/// <param name="ignoreDamageResistances">
/// Whether to ignore resistances when taking damage. Healing always ignores resistances, regardless of this
/// input.
/// <param name="ignoreResistances">
/// Whether to ignore resistances.
/// Healing always ignores resistances, regardless of this input.
/// </param>
/// <param name="source">Entity that dealt or healed the damage, if any.</param>
/// <param name="extraParams">
/// Extra parameters that some components may require,
/// such as a specific limb to target.
/// </param>
/// <returns>
/// Returns false if the given group is not applicable or no damage change occurred; true otherwise.
/// Returns false if the given class is not supported or improper
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
/// </returns>
<<<<<<< HEAD
<<<<<<< refs/remotes/origin/master
bool ChangeDamage(
DamageClass @class,
int amount,
@@ -277,110 +117,28 @@ namespace Content.Shared.Damage.Components
/// <summary>
/// Forcefully sets the specified <see cref="DamageType"/> to the given
/// value, ignoring resistance values.
=======
bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false);
/// <summary>
=======
bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false);
/// <summary>
>>>>>>> refactor-damageablecomponent
/// Forcefully sets the specified <see cref="DamageTypePrototype"/> to the given value, ignoring resistance
/// values.
/// </summary>
/// <param name="type">Type of damage being set.</param>
<<<<<<< HEAD
/// <param name="type">Type of damage being changed.</param>
/// <param name="newValue">New damage value to be set.</param>
/// <param name="source">Entity that set the new damage value.</param>
/// <param name="extraParams">
/// Extra parameters that some components may require,
/// such as a specific limb to target.
/// </param>
/// <returns>
/// Returns false if a given type is not supported or a negative value is provided; true otherwise.
/// Returns false if the given type is not supported or improper
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
/// </returns>
bool TrySetDamage(DamageTypePrototype type, int newValue);
/// <summary>
/// Forcefully sets all damage types in a specified damage group using <see cref="TrySetDamage"></see>.
/// </summary>
/// <remarks>
/// Note that the actual damage of this group will be equal to the given value times the number damage group
/// members that this container supports.
/// </remarks>
/// <param name="group">Group of damage being set.</param>
/// <param name="newValue">New damage value to be set.</param>
/// <returns>
/// Returns false if the given group is not applicable or a negative value is provided; true otherwise.
/// </returns>
bool TrySetDamage(DamageGroupPrototype group, int newValue);
/// <summary>
/// Sets all supported damage types to specified value using <see cref="TrySetDamage"></see>.
>>>>>>> Refactor damageablecomponent update (#4406)
/// </summary>
/// <param name="newValue">New damage value to be set.</param>
/// <returns>
/// Returns false if a negative value is provided; true otherwise.
/// </returns>
<<<<<<< refs/remotes/origin/master
bool SetDamage(
DamageType type,
int newValue,
IEntity? source = null,
DamageChangeParams? extraParams = null);
=======
bool TrySetAllDamage(int newValue);
/// <summary>
/// Returns true if the given damage group is applicable to this damage container.
/// Sets all damage values to zero.
/// </summary>
=======
/// <param name="newValue">New damage value to be set.</param>
/// <returns>
/// Returns false if a given type is not supported or a negative value is provided; true otherwise.
/// </returns>
bool TrySetDamage(DamageTypePrototype type, int newValue);
/// <summary>
/// Forcefully sets all damage types in a specified damage group using <see cref="TrySetDamage"></see>.
/// </summary>
/// <remarks>
/// Note that the actual damage of this group will be equal to the given value times the number damage group
/// members that this container supports.
/// </remarks>
/// <param name="group">Group of damage being set.</param>
/// <param name="newValue">New damage value to be set.</param>
/// <returns>
/// Returns false if the given group is not applicable or a negative value is provided; true otherwise.
/// </returns>
bool TrySetDamage(DamageGroupPrototype group, int newValue);
/// <summary>
/// Sets all supported damage types to specified value using <see cref="TrySetDamage"></see>.
/// </summary>
/// <param name="newValue">New damage value to be set.</param>
/// <returns>
/// Returns false if a negative value is provided; true otherwise.
/// </returns>
bool TrySetAllDamage(int newValue);
/// <summary>
/// Returns true if the given damage group is applicable to this damage container.
/// </summary>
>>>>>>> refactor-damageablecomponent
public bool IsApplicableDamageGroup(DamageGroupPrototype group);
/// <summary>
/// Returns true if the given damage group is fully supported by this damage container.
/// </summary>
public bool IsFullySupportedDamageGroup(DamageGroupPrototype group);
<<<<<<< HEAD
>>>>>>> Refactor damageablecomponent update (#4406)
=======
>>>>>>> refactor-damageablecomponent
/// <summary>
/// Returns true if the given damage type is supported by this damage container.
/// </summary>
public bool IsSupportedDamageType(DamageTypePrototype type);
void Heal();
/// <summary>
/// Invokes the HealthChangedEvent with the current values of health.