Update DamageableSystem to modern standards (#39417)
* Update DamageableSystem to modern standards * DamageContainerId -> DamageContainerID with lint flag * Replace strings with protoids * Make CVar subscription declarations all consistently whitespaced * ChangeDamage -> TryChangeDamage, cope with C# jank * Revert event signature changes * Restore a comment * Re-add two queries * Init the queries * Use appearanceQuery in DamageChanged * Use damageableQuery in TryChangeDamage * Use damageableQuery in SetDamageModifierSetId * Final cleanup, fix sandboxing * Rectify ExplosionSystem:::ProcessEntity's call to TryChangeDamage * Re-organize DamageableSystem * first big fuck you breaking change. * THATS A LOT OF DAMAGE!!! * Fix test fails * test fixes 2 * push it --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
cf66dd7b35
commit
cdbe92d37d
235
Content.Shared/Damage/Systems/DamageableSystem.API.cs
Normal file
235
Content.Shared/Damage/Systems/DamageableSystem.API.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Damage.Systems;
|
||||
|
||||
public sealed partial class DamageableSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Directly sets the damage specifier of a damageable component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Useful for some unfriendly folk. Also ensures that cached values are updated and that a damage changed
|
||||
/// event is raised.
|
||||
/// </remarks>
|
||||
public void SetDamage(Entity<DamageableComponent?> ent, DamageSpecifier damage)
|
||||
{
|
||||
if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
ent.Comp.Damage = damage;
|
||||
|
||||
OnEntityDamageChanged((ent, ent.Comp));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies damage specified via a <see cref="DamageSpecifier"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
|
||||
/// function just applies the container's resistances (unless otherwise specified) and then changes the
|
||||
/// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// If the attempt was successful or not.
|
||||
/// </returns>
|
||||
public bool TryChangeDamage(
|
||||
Entity<DamageableComponent?> ent,
|
||||
DamageSpecifier damage,
|
||||
bool ignoreResistances = false,
|
||||
bool interruptsDoAfters = true,
|
||||
EntityUid? origin = null,
|
||||
bool ignoreGlobalModifiers = false
|
||||
)
|
||||
{
|
||||
//! Empty just checks if the DamageSpecifier is _literally_ empty, as in, is internal dictionary of damage types is empty.
|
||||
// If you deal 0.0 of some damage type, Empty will be false!
|
||||
return !TryChangeDamage(ent, damage, out _, ignoreResistances, interruptsDoAfters, origin, ignoreGlobalModifiers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies damage specified via a <see cref="DamageSpecifier"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
|
||||
/// function just applies the container's resistances (unless otherwise specified) and then changes the
|
||||
/// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// If the attempt was successful or not.
|
||||
/// </returns>
|
||||
public bool TryChangeDamage(
|
||||
Entity<DamageableComponent?> ent,
|
||||
DamageSpecifier damage,
|
||||
out DamageSpecifier newDamage,
|
||||
bool ignoreResistances = false,
|
||||
bool interruptsDoAfters = true,
|
||||
EntityUid? origin = null,
|
||||
bool ignoreGlobalModifiers = false
|
||||
)
|
||||
{
|
||||
//! Empty just checks if the DamageSpecifier is _literally_ empty, as in, is internal dictionary of damage types is empty.
|
||||
// If you deal 0.0 of some damage type, Empty will be false!
|
||||
newDamage = ChangeDamage(ent, damage, ignoreResistances, interruptsDoAfters, origin, ignoreGlobalModifiers);
|
||||
return !damage.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies damage specified via a <see cref="DamageSpecifier"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="DamageSpecifier"/> is effectively just a dictionary of damage types and damage values. This
|
||||
/// function just applies the container's resistances (unless otherwise specified) and then changes the
|
||||
/// stored damage data. Division of group damage into types is managed by <see cref="DamageSpecifier"/>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// The actual amount of damage taken, as a DamageSpecifier.
|
||||
/// </returns>
|
||||
public DamageSpecifier ChangeDamage(
|
||||
Entity<DamageableComponent?> ent,
|
||||
DamageSpecifier damage,
|
||||
bool ignoreResistances = false,
|
||||
bool interruptsDoAfters = true,
|
||||
EntityUid? origin = null,
|
||||
bool ignoreGlobalModifiers = false
|
||||
)
|
||||
{
|
||||
var damageDone = new DamageSpecifier();
|
||||
|
||||
if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
|
||||
return damageDone;
|
||||
|
||||
if (damage.Empty)
|
||||
return damageDone;
|
||||
|
||||
var before = new BeforeDamageChangedEvent(damage, origin);
|
||||
RaiseLocalEvent(ent, ref before);
|
||||
|
||||
if (before.Cancelled)
|
||||
return damageDone;
|
||||
|
||||
// Apply resistances
|
||||
if (!ignoreResistances)
|
||||
{
|
||||
if (
|
||||
ent.Comp.DamageModifierSetId != null &&
|
||||
_prototypeManager.Resolve(ent.Comp.DamageModifierSetId, out var modifierSet)
|
||||
)
|
||||
damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
|
||||
|
||||
// TODO DAMAGE
|
||||
// byref struct event.
|
||||
var ev = new DamageModifyEvent(damage, origin);
|
||||
RaiseLocalEvent(ent, ev);
|
||||
damage = ev.Damage;
|
||||
|
||||
if (damage.Empty)
|
||||
return damageDone;
|
||||
}
|
||||
|
||||
if (!ignoreGlobalModifiers)
|
||||
damage = ApplyUniversalAllModifiers(damage);
|
||||
|
||||
|
||||
damageDone.DamageDict.EnsureCapacity(damage.DamageDict.Count);
|
||||
|
||||
var dict = ent.Comp.Damage.DamageDict;
|
||||
foreach (var (type, value) in damage.DamageDict)
|
||||
{
|
||||
// CollectionsMarshal my beloved.
|
||||
if (!dict.TryGetValue(type, out var oldValue))
|
||||
continue;
|
||||
|
||||
var newValue = FixedPoint2.Max(FixedPoint2.Zero, oldValue + value);
|
||||
if (newValue == oldValue)
|
||||
continue;
|
||||
|
||||
dict[type] = newValue;
|
||||
damageDone.DamageDict[type] = newValue - oldValue;
|
||||
}
|
||||
|
||||
if (!damageDone.Empty)
|
||||
OnEntityDamageChanged((ent, ent.Comp), damageDone, interruptsDoAfters, origin);
|
||||
|
||||
return damageDone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the two universal "All" modifiers, if set.
|
||||
/// Individual damage source modifiers are set in their respective code.
|
||||
/// </summary>
|
||||
/// <param name="damage">The damage to be changed.</param>
|
||||
public DamageSpecifier ApplyUniversalAllModifiers(DamageSpecifier damage)
|
||||
{
|
||||
// Checks for changes first since they're unlikely in normal play.
|
||||
if (
|
||||
MathHelper.CloseToPercent(UniversalAllDamageModifier, 1f) &&
|
||||
MathHelper.CloseToPercent(UniversalAllHealModifier, 1f)
|
||||
)
|
||||
return damage;
|
||||
|
||||
foreach (var (key, value) in damage.DamageDict)
|
||||
{
|
||||
if (value == 0)
|
||||
continue;
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
damage.DamageDict[key] *= UniversalAllDamageModifier;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value < 0)
|
||||
damage.DamageDict[key] *= UniversalAllHealModifier;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
public void ClearAllDamage(Entity<DamageableComponent?> ent)
|
||||
{
|
||||
SetAllDamage(ent, FixedPoint2.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all damage types supported by a <see cref="Components.DamageableComponent"/> to the specified value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does nothing If the given damage value is negative.
|
||||
/// </remarks>
|
||||
public void SetAllDamage(Entity<DamageableComponent?> ent, FixedPoint2 newValue)
|
||||
{
|
||||
if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
if (newValue < 0)
|
||||
return;
|
||||
|
||||
foreach (var type in ent.Comp.Damage.DamageDict.Keys)
|
||||
{
|
||||
ent.Comp.Damage.DamageDict[type] = newValue;
|
||||
}
|
||||
|
||||
// Setting damage does not count as 'dealing' damage, even if it is set to a larger value, so we pass an
|
||||
// empty damage delta.
|
||||
OnEntityDamageChanged((ent, ent.Comp), new DamageSpecifier());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set's the damage modifier set prototype for this entity.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity we're setting the modifier set of.</param>
|
||||
/// <param name="damageModifierSetId">The prototype we're setting.</param>
|
||||
public void SetDamageModifierSetId(Entity<DamageableComponent?> ent, ProtoId<DamageModifierSetPrototype>? damageModifierSetId)
|
||||
{
|
||||
if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
ent.Comp.DamageModifierSetId = damageModifierSetId;
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user