diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs
index deaa5bc834..7fec07107f 100644
--- a/Content.Server/Body/Systems/RespiratorSystem.cs
+++ b/Content.Server/Body/Systems/RespiratorSystem.cs
@@ -20,8 +20,8 @@ using Content.Shared.Database;
using Content.Shared.EntityConditions;
using Content.Shared.EntityConditions.Conditions.Body;
using Content.Shared.EntityEffects;
-using Content.Shared.EntityEffects.Effects;
using Content.Shared.EntityEffects.Effects.Body;
+using Content.Shared.EntityEffects.Effects.Damage;
using Content.Shared.Mobs.Systems;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
diff --git a/Content.Shared/Damage/Systems/DamageableSystem.API.cs b/Content.Shared/Damage/Systems/DamageableSystem.API.cs
index c39924ae2a..4670886c54 100644
--- a/Content.Shared/Damage/Systems/DamageableSystem.API.cs
+++ b/Content.Shared/Damage/Systems/DamageableSystem.API.cs
@@ -1,3 +1,5 @@
+using System.Linq;
+using System.Net.Sockets;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
@@ -230,6 +232,185 @@ public sealed partial class DamageableSystem
return damageDone;
}
+ ///
+ /// Will reduce the damage on the entity exactly by as close as equally distributed among all damage types the entity has.
+ /// If one of the damage types of the entity is too low. it will heal that completly and distribute the excess healing among the other damage types.
+ /// If the is larger than the total damage of the entity then it just clears all damage.
+ ///
+ /// entity to be healed
+ /// how much to heal. value has to be negative to heal
+ /// from which group to heal. if null, heal from all groups
+ /// who did the healing
+ public DamageSpecifier HealEvenly(
+ Entity ent,
+ FixedPoint2 amount,
+ ProtoId? group = null,
+ EntityUid? origin = null,
+ // Begin DeltaV additions - Adapt to shitmed changes
+ TargetBodyPart? targetPart = null,
+ bool doPartDamage = true,
+ bool onlyDamageParts = false // Fix EvenHealing on Limbs && Standardize PartDamage.
+ // End DeltaV additions - Adapt to shitmed changes
+ )
+ {
+ var damageChange = new DamageSpecifier();
+
+ if (!_damageableQuery.Resolve(ent, ref ent.Comp, false) || amount >= 0)
+ return damageChange;
+
+ // Get our total damage, or heal if we're below a certain amount.
+ if (!TryGetDamageGreaterThan((ent, ent.Comp), -amount, out var damage, group))
+ return ChangeDamage(ent, -damage, true, false, origin);
+
+ // make sure damageChange has the same damage types as damage
+ damageChange.DamageDict.EnsureCapacity(damage.DamageDict.Count);
+ foreach (var type in damage.DamageDict.Keys)
+ {
+ damageChange.DamageDict.Add(type, FixedPoint2.Zero);
+ }
+
+ var remaining = -amount;
+ var keys = damage.DamageDict.Keys.ToList();
+
+ while (remaining > 0)
+ {
+ var count = keys.Count;
+ // We do this to ensure that we always round up when dividing to avoid excess loops.
+ // We already have logic to prevent healing more than we have.
+ var maxHeal = count == 1 ? remaining : (remaining + FixedPoint2.Epsilon * (count - 1)) / count;
+
+ // Iterate backwards since we're removing items.
+ for (var i = count - 1; i >= 0; i--)
+ {
+ var type = keys[i];
+ // This is the amount we're trying to heal, capped by maxHeal
+ var heal = damage.DamageDict[type] + damageChange.DamageDict[type];
+
+ // Don't go above max, if we don't go above max
+ if (heal > maxHeal)
+ heal = maxHeal;
+ // If we're not above max, we will heal it fully and don't need to enumerate anymore!
+ else
+ keys.RemoveAt(i);
+
+ if (heal >= remaining)
+ {
+ // Don't remove more than we can remove. Prevents us from healing more than we'd expect...
+ damageChange.DamageDict[type] -= remaining;
+ remaining = FixedPoint2.Zero;
+ break;
+ }
+
+ remaining -= heal;
+ damageChange.DamageDict[type] -= heal;
+ }
+ }
+
+ return ChangeDamage(ent, damageChange, true, false, origin, targetPart: targetPart, doPartDamage: doPartDamage, onlyDamageParts: onlyDamageParts); // DeltaV - Adapt to shitmed
+ }
+
+ ///
+ /// Will reduce the damage on the entity exactly by distributed by weight among all damage types the entity has.
+ /// (the weight is how much damage of the type there is)
+ /// If the is larger than the total damage of the entity then it just clears all damage.
+ ///
+ /// entity to be healed
+ /// how much to heal. value has to be negative to heal
+ /// from which group to heal. if null, heal from all groups
+ /// who did the healing
+ public DamageSpecifier HealDistributed(
+ Entity ent,
+ FixedPoint2 amount,
+ ProtoId? group = null,
+ EntityUid? origin = null)
+ {
+ var damageChange = new DamageSpecifier();
+
+ if (!_damageableQuery.Resolve(ent, ref ent.Comp, false) || amount >= 0)
+ return damageChange;
+
+ // Get our total damage, or heal if we're below a certain amount.
+ if (!TryGetDamageGreaterThan((ent, ent.Comp), -amount, out var damage, group))
+ return ChangeDamage(ent, -damage, true, false, origin);
+
+ // make sure damageChange has the same damage types as damageEntity
+ damageChange.DamageDict.EnsureCapacity(damage.DamageDict.Count);
+ var total = damage.GetTotal();
+
+ // heal weighted by the damage of that type
+ foreach (var (type, value) in damage.DamageDict)
+ {
+ damageChange.DamageDict.Add(type, value / total * amount);
+ }
+
+ return ChangeDamage(ent, damageChange, true, false, origin);
+ }
+
+ ///
+ /// Tries to get damage from an entity with an optional group specifier.
+ ///
+ /// Entity we're checking the damage on
+ /// Amount we want the damage to be greater than ideally
+ /// Damage specifier we're returning with
+ /// An optional group, note that if it fails to index it will just use all damage.
+ /// True if the total damage is greater than the specified amount
+ public bool TryGetDamageGreaterThan(Entity ent,
+ FixedPoint2 amount,
+ out DamageSpecifier damage,
+ ProtoId? group = null)
+ {
+ // get the damage should be healed (either all or only from one group)
+ damage = group == null ? GetDamage(ent) : GetDamage(ent, group.Value);
+
+ // If trying to heal more than the total damage of damageEntity just heal everything
+ return damage.GetTotal() > amount;
+ }
+
+ ///
+ /// Returns a with all positive damage of the entity from the group specified
+ ///
+ /// entity with damage
+ /// group of damage to get values from
+ ///
+ public DamageSpecifier GetDamage(Entity ent, ProtoId group)
+ {
+ // No damage if no group exists...
+ if (!_prototypeManager.Resolve(group, out var groupProto))
+ return new DamageSpecifier();
+
+ var damage = new DamageSpecifier();
+ damage.DamageDict.EnsureCapacity(groupProto.DamageTypes.Count);
+
+ foreach (var damageId in groupProto.DamageTypes)
+ {
+ if (!ent.Comp.Damage.DamageDict.TryGetValue(damageId, out var value))
+ continue;
+ if (value > FixedPoint2.Zero)
+ damage.DamageDict.Add(damageId, value);
+ }
+
+ return damage;
+ }
+
+ ///
+ /// Returns a with all positive damage of the entity
+ ///
+ /// entity with damage
+ ///
+ public DamageSpecifier GetDamage(Entity ent)
+ {
+ var damage = new DamageSpecifier();
+ damage.DamageDict.EnsureCapacity(ent.Comp.Damage.DamageDict.Count);
+
+ foreach (var (damageId, value) in ent.Comp.Damage.DamageDict)
+ {
+ if (value > FixedPoint2.Zero)
+ damage.DamageDict.Add(damageId, value);
+ }
+
+ return damage;
+ }
+
///
/// Applies the two universal "All" modifiers, if set.
/// Individual damage source modifiers are set in their respective code.
diff --git a/Content.Shared/EntityEffects/Effects/Damage/DistributedHealthChangeEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/Damage/DistributedHealthChangeEntityEffectSystem.cs
new file mode 100644
index 0000000000..66911d7785
--- /dev/null
+++ b/Content.Shared/EntityEffects/Effects/Damage/DistributedHealthChangeEntityEffectSystem.cs
@@ -0,0 +1,91 @@
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Prototypes;
+using Content.Shared.Damage.Systems;
+using Content.Shared.FixedPoint;
+using Content.Shared.Localizations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.Damage;
+
+///
+/// Heal the damage types in a damage group by up to a specified total on this entity.
+/// The amount healed per type is weighted by the amount of damage for that type scaling linearly.
+/// Total adjustment is modified by scale.
+///
+///
+public sealed partial class DistributedHealthChangeEntityEffectSystem : EntityEffectSystem
+{
+ [Dependency] private readonly DamageableSystem _damageable = default!;
+
+ protected override void Effect(Entity entity, ref EntityEffectEvent args)
+ {
+ foreach (var (group, amount) in args.Effect.Damage)
+ {
+ _damageable.HealDistributed(entity.AsNullable(), amount * args.Scale, group);
+ }
+ }
+}
+
+///
+public sealed partial class DistributedHealthChange : EntityEffectBase
+{
+ ///
+ /// Damage to heal, collected into entire damage groups.
+ ///
+ [DataField(required: true)]
+ public Dictionary, FixedPoint2> Damage = new();
+
+ ///
+ /// Should this effect ignore damage modifiers?
+ ///
+ [DataField]
+ public bool IgnoreResistances = true;
+
+ public override string EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ var damages = new List();
+ var heals = false;
+ var deals = false;
+
+ var damagableSystem = entSys.GetEntitySystem();
+ var universalReagentDamageModifier = damagableSystem.UniversalReagentDamageModifier;
+ var universalReagentHealModifier = damagableSystem.UniversalReagentHealModifier;
+
+ foreach (var (group, amount) in Damage)
+ {
+ var groupProto = prototype.Index(group);
+
+ var sign = FixedPoint2.Sign(amount);
+ float mod;
+
+ switch (sign)
+ {
+ case < 0:
+ heals = true;
+ mod = universalReagentHealModifier;
+ break;
+ case > 0:
+ deals = true;
+ mod = universalReagentDamageModifier;
+ break;
+ default:
+ continue; // Don't need to show damage types of 0...
+ }
+
+ damages.Add(
+ Loc.GetString("health-change-display",
+ ("kind", groupProto.LocalizedName),
+ ("amount", MathF.Abs(amount.Float() * mod)),
+ ("deltasign", sign)
+ ));
+ }
+
+ // We use health change since in practice it's not even and distributed is a mouthful.
+ // Also because healing groups not using even or distributed healing should be kill.
+ var healsordeals = heals ? deals ? "both" : "heals" : deals ? "deals" : "none";
+ return Loc.GetString("entity-effect-guidebook-health-change",
+ ("chance", Probability),
+ ("changes", ContentLocalizationManager.FormatList(damages)),
+ ("healsordeals", healsordeals));
+ }
+}
diff --git a/Content.Shared/EntityEffects/Effects/EvenHealthChangeEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/Damage/EvenHealthChangeEntityEffectSystem.cs
similarity index 51%
rename from Content.Shared/EntityEffects/Effects/EvenHealthChangeEntityEffectSystem.cs
rename to Content.Shared/EntityEffects/Effects/Damage/EvenHealthChangeEntityEffectSystem.cs
index 273a4bc961..010193dc22 100644
--- a/Content.Shared/EntityEffects/Effects/EvenHealthChangeEntityEffectSystem.cs
+++ b/Content.Shared/EntityEffects/Effects/Damage/EvenHealthChangeEntityEffectSystem.cs
@@ -1,102 +1,48 @@
-// DeltaV Start - Fix EvenHealing with Limbs.
-using System.Linq;
+// DeltaV Start - Fix EvenHealing with Limbs.
using Content.Shared._Shitmed.Targeting;
using Content.Shared.Body.Systems;
// DeltaV End - Fix EvenHealing with Limbs.
-using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
-namespace Content.Shared.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects.Damage;
///
-/// Evenly adjust the damage types in a damage group by up to a specified total on this entity.
+/// Evenly heal the damage types in a damage group by up to a specified total on this entity.
/// Total adjustment is modified by scale.
///
///
public sealed partial class EvenHealthChangeEntityEffectSystem : EntityEffectSystem
{
[Dependency] private readonly DamageableSystem _damageable = default!;
- [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedBodySystem _body = default!; // DeltaV
- [Dependency] private readonly EntityManager _ent = default!; // DeltaV
protected override void Effect(Entity entity, ref EntityEffectEvent args)
{
- // DeltaV - Even Healing with Limbs
- var damageSpec = GetDamageSpec(entity.Owner, ref args); // DeltaV - Basically moved this to a private method
-
- damageSpec *= args.Scale;
-
-
- _damageable.TryChangeDamage(
- entity.AsNullable(),
- damageSpec,
- args.Effect.IgnoreResistances,
- interruptsDoAfters: false,
- doPartDamage: false); // DeltaV - Even Healing with Limbs
-
- var bodyParts = SharedTargetingSystem.GetValidParts();
- foreach (var bodyPart in bodyParts)
- {
- var (targetType, targetSymmetry) = _body.ConvertTargetBodyPart(bodyPart);
- if (_body.GetBodyChildrenOfType(entity, targetType, symmetry: targetSymmetry) is { } part)
- {
- var dspec = GetDamageSpec(part.FirstOrDefault().Id, ref args);
-
- if (dspec.GetTotal() == 0)
- continue;
-
- _damageable.TryChangeDamage(
- entity.AsNullable(),
- dspec * args.Scale,
- args.Effect.IgnoreResistances,
- interruptsDoAfters: false,
- targetPart: bodyPart,
- onlyDamageParts: true,
- canSever: false);
- }
- }
- // END DeltaV
- }
-
- ///
- /// DeltaV - Returns a damage spec for a specific entity with DamageableComponent.
- ///
- ///
- ///
- ///
- private DamageSpecifier GetDamageSpec(Entity entity, ref EntityEffectEvent args)
- {
- var damageSpec = new DamageSpecifier();
- if (!_ent.TryGetComponent(entity, out var damageable))
- return damageSpec;
-
foreach (var (group, amount) in args.Effect.Damage)
{
- var groupProto = _proto.Index(group);
- var groupDamage = new Dictionary();
- foreach (var damageId in groupProto.DamageTypes)
- {
- var damageAmount = damageable.Damage.DamageDict.GetValueOrDefault(damageId);
- if (damageAmount != FixedPoint2.Zero)
- groupDamage.Add(damageId, damageAmount);
- }
+ // Begin DeltaV Additions - Limb even healing
+ _damageable.HealEvenly(entity.AsNullable(), amount * args.Scale, group, doPartDamage: false);
- var sum = groupDamage.Values.Sum();
- foreach (var (damageId, damageAmount) in groupDamage)
+ var bodyParts = SharedTargetingSystem.GetValidParts();
+ foreach (var bodyPart in bodyParts)
{
- var existing = damageSpec.DamageDict.GetOrNew(damageId);
- damageSpec.DamageDict[damageId] = existing + damageAmount / sum * amount;
+ var (targetType, targetSymmetry) = _body.ConvertTargetBodyPart(bodyPart);
+ if (_body.GetBodyChildrenOfType(entity, targetType, symmetry: targetSymmetry) is { } part)
+ {
+ _damageable.HealEvenly(
+ entity.AsNullable(),
+ amount * args.Scale,
+ targetPart: bodyPart,
+ onlyDamageParts: true);
+ }
}
+ // End DeltaV Additions - Limb even healing
}
-
- return damageSpec;
}
}
diff --git a/Content.Shared/EntityEffects/Effects/HealthChangeEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/Damage/HealthChangeEntityEffectSystem.cs
similarity index 95%
rename from Content.Shared/EntityEffects/Effects/HealthChangeEntityEffectSystem.cs
rename to Content.Shared/EntityEffects/Effects/Damage/HealthChangeEntityEffectSystem.cs
index 3e6fe8330b..b23ab25f13 100644
--- a/Content.Shared/EntityEffects/Effects/HealthChangeEntityEffectSystem.cs
+++ b/Content.Shared/EntityEffects/Effects/Damage/HealthChangeEntityEffectSystem.cs
@@ -6,7 +6,7 @@ using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
using Robust.Shared.Prototypes;
-namespace Content.Shared.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects.Damage;
///
/// Adjust the damages on this entity by specified amounts.
@@ -15,7 +15,7 @@ namespace Content.Shared.EntityEffects.Effects;
///
public sealed partial class HealthChangeEntityEffectSystem : EntityEffectSystem
{
- [Dependency] private readonly Damage.Systems.DamageableSystem _damageable = default!;
+ [Dependency] private readonly DamageableSystem _damageable = default!;
protected override void Effect(Entity entity, ref EntityEffectEvent args)
{
@@ -94,4 +94,4 @@ public sealed partial class HealthChange : EntityEffectBase
("changes", ContentLocalizationManager.FormatList(damages)),
("healsordeals", healsordeals));
}
-}
\ No newline at end of file
+}