Initial decoupling pass on Damageable (#43103)

* Initial decoupling pass on Damageable

* ope

* e

* mroaw

* ok sure on the medibot thing

* no medibot bounds

* okidooks

* arcryox era

* cryox moment
This commit is contained in:
pathetic meowmeow
2026-03-06 23:13:50 -05:00
committed by GitHub
parent c2681cdb17
commit b2b547b806
83 changed files with 281 additions and 513 deletions
@@ -1,4 +1,6 @@
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Client.Damage;
@@ -55,7 +57,7 @@ public sealed partial class DamageVisualsComponent : Component
/// (for example, Brute), and has a value
/// of a DamageVisualizerSprite (see below)
/// </summary>
[DataField("damageOverlayGroups")] public Dictionary<string, DamageVisualizerSprite>? DamageOverlayGroups;
[DataField("damageOverlayGroups")] public Dictionary<ProtoId<DamageGroupPrototype>, DamageVisualizerSprite>? DamageOverlayGroups;
/// <summary>
/// Sets if you want sprites to overlay the
@@ -84,7 +86,7 @@ public sealed partial class DamageVisualsComponent : Component
/// what kind of damage combination
/// you would want, on which threshold.
/// </remarks>
[DataField("damageGroup")] public string? DamageGroup;
[DataField("damageGroup")] public ProtoId<DamageGroupPrototype>? DamageGroup;
/// <summary>
/// Set this if you want incoming damage to be
+9 -8
View File
@@ -2,6 +2,7 @@ using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
@@ -28,6 +29,7 @@ namespace Content.Client.Damage;
public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponent>
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -174,7 +176,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
// See if that group is in our entity's damage container.
else if (!damageVisComp.Overlay && damageVisComp.DamageGroup != null)
{
if (!damageContainer.SupportedGroups.Contains(damageVisComp.DamageGroup))
if (!damageContainer.SupportedGroups.Contains(damageVisComp.DamageGroup.Value))
{
Log.Error($"Damage keys were invalid for entity {entity}.");
damageVisComp.Valid = false;
@@ -384,7 +386,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
if (!AppearanceSystem.TryGetData<DamageVisualizerGroupData>(uid, DamageVisualizerKeys.DamageUpdateGroups,
out var data, component))
{
data = new DamageVisualizerGroupData(Comp<DamageableComponent>(uid).DamagePerGroup.Keys.ToList());
data = new DamageVisualizerGroupData(_damageable.GetDamagePerGroup(uid).Keys.ToList());
}
UpdateDamageVisuals(data.GroupList, (uid, damageComponent, spriteComponent, damageVisComp));
@@ -486,11 +488,10 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
/// </summary>
private void UpdateDamageVisuals(Entity<DamageableComponent, SpriteComponent, DamageVisualsComponent> entity)
{
var damageComponent = entity.Comp1;
var spriteComponent = entity.Comp2;
var damageVisComp = entity.Comp3;
if (!CheckThresholdBoundary(damageComponent.TotalDamage, damageVisComp.LastDamageThreshold, damageVisComp, out var threshold))
if (!CheckThresholdBoundary(_damageable.GetTotalDamage(entity.AsNullable()), damageVisComp.LastDamageThreshold, damageVisComp, out var threshold))
return;
damageVisComp.LastDamageThreshold = threshold;
@@ -513,11 +514,11 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
/// according to the list of damage groups
/// passed into it.
/// </summary>
private void UpdateDamageVisuals(List<string> delta, Entity<DamageableComponent, SpriteComponent, DamageVisualsComponent> entity)
private void UpdateDamageVisuals(List<ProtoId<DamageGroupPrototype>> delta, Entity<DamageableComponent, SpriteComponent, DamageVisualsComponent> entity)
{
var damageComponent = entity.Comp1;
var spriteComponent = entity.Comp2;
var damageVisComp = entity.Comp3;
var damage = _damageable.GetAllDamage((entity.Owner, entity.Comp1));
foreach (var damageGroup in delta)
{
@@ -525,7 +526,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
continue;
if (!_prototypeManager.TryIndex<DamageGroupPrototype>(damageGroup, out var damageGroupPrototype)
|| !damageComponent.Damage.TryGetDamageInGroup(damageGroupPrototype, out var damageTotal))
|| !damage.TryGetDamageInGroup(damageGroupPrototype, out var damageTotal))
continue;
if (!damageVisComp.LastThresholdPerGroup.TryGetValue(damageGroup, out var lastThreshold)
@@ -590,7 +591,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
}
else if (damageVisComp.DamageGroup != null)
{
UpdateDamageVisuals(new List<string>() { damageVisComp.DamageGroup }, entity);
UpdateDamageVisuals(new() { damageVisComp.DamageGroup.Value }, entity);
}
else if (damageVisComp.DamageOverlay != null)
{
@@ -3,6 +3,7 @@ using System.Numerics;
using Content.Shared.Atmos;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
@@ -30,6 +31,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
private readonly SpriteSystem _spriteSystem;
private readonly IPrototypeManager _prototypes;
private readonly IResourceCache _cache;
private readonly DamageableSystem _damageable;
public HealthAnalyzerControl()
{
@@ -40,6 +42,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
_spriteSystem = _entityManager.System<SpriteSystem>();
_prototypes = dependencies.Resolve<IPrototypeManager>();
_cache = dependencies.Resolve<IResourceCache>();
_damageable = _entityManager.System<DamageableSystem>();
}
public void Populate(HealthAnalyzerUiState state)
@@ -101,7 +104,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
// Total Damage
DamageLabel.Text = damageable.TotalDamage.ToString();
DamageLabel.Text = _damageable.GetTotalDamage(target.Value).ToString();
// Alerts
@@ -132,10 +135,11 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
// Damage Groups
var damageSortedGroups =
damageable.DamagePerGroup.OrderByDescending(damage => damage.Value)
_damageable.GetDamagePerGroup(target.Value)
.OrderByDescending(damage => damage.Value)
.ToDictionary(x => x.Key, x => x.Value);
var damagePerType = damageable.Damage.DamageDict;
var damagePerType = _damageable.GetAllDamage(target.Value).DamageDict;
DrawDiagnosticGroups(damageSortedGroups, damagePerType);
}
@@ -152,7 +156,7 @@ public sealed partial class HealthAnalyzerControl : BoxContainer
}
private void DrawDiagnosticGroups(
Dictionary<string, FixedPoint2> groups,
Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> groups,
IReadOnlyDictionary<ProtoId<DamageTypePrototype>, FixedPoint2> damageDict)
{
GroupsContainer.RemoveAllChildren();
@@ -4,6 +4,7 @@ using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.EntityConditions.Conditions;
using Content.Shared.FixedPoint;
using Content.Shared.Medical.Cryogenics;
@@ -86,9 +87,7 @@ public sealed partial class CryoPodWindow : FancyWindow
// Health analyzer
var maybePatient = _entityManager.GetEntity(msg.Health.TargetEntity);
var hasPatient = msg.Health.TargetEntity.HasValue;
var hasDamage = (hasPatient
&& _entityManager.TryGetComponent(maybePatient, out DamageableComponent? damageable)
&& damageable.TotalDamage > 0);
var hasDamage = hasPatient && msg.HasDamage;
NoDamageText.Visible = (hasPatient && !hasDamage);
HealthSection.Visible = hasPatient;
@@ -2,6 +2,7 @@ using System.Numerics;
using Content.Client.StatusIcon;
using Content.Client.UserInterface.Systems;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
@@ -30,6 +31,7 @@ public sealed class EntityHealthBarOverlay : Overlay
private readonly StatusIconSystem _statusIconSystem;
private readonly SpriteSystem _spriteSystem;
private readonly ProgressColorSystem _progressColor;
private readonly DamageableSystem _damageable;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
@@ -46,6 +48,7 @@ public sealed class EntityHealthBarOverlay : Overlay
_statusIconSystem = _entManager.System<StatusIconSystem>();
_spriteSystem = _entManager.System<SpriteSystem>();
_progressColor = _entManager.System<ProgressColorSystem>();
_damageable = _entManager.System<DamageableSystem>();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -129,16 +132,17 @@ public sealed class EntityHealthBarOverlay : Overlay
/// </summary>
private (float ratio, bool inCrit)? CalcProgress(EntityUid uid, MobStateComponent component, DamageableComponent dmg, MobThresholdsComponent thresholds)
{
var totalDamage = _damageable.GetTotalDamage((uid, dmg));
if (_mobStateSystem.IsAlive(uid, component))
{
if (dmg.HealthBarThreshold != null && dmg.TotalDamage < dmg.HealthBarThreshold)
if (dmg.HealthBarThreshold != null && totalDamage < dmg.HealthBarThreshold)
return null;
if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholds) &&
!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Dead, out threshold, thresholds))
return (1, false);
var ratio = 1 - ((FixedPoint2)(dmg.TotalDamage / threshold)).Float();
var ratio = 1 - ((FixedPoint2)(totalDamage / threshold)).Float();
return (ratio, false);
}
@@ -150,7 +154,7 @@ public sealed class EntityHealthBarOverlay : Overlay
return (1, true);
}
var ratio = 1 - ((dmg.TotalDamage - critThreshold) / (deadThreshold - critThreshold)).Value.Float();
var ratio = 1 - ((totalDamage - critThreshold) / (deadThreshold - critThreshold)).Value.Float();
return (ratio, true);
}
@@ -1,4 +1,5 @@
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
@@ -22,6 +23,7 @@ public sealed class DamageOverlayUiController : UIController
[UISystemDependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
[UISystemDependency] private readonly StatusEffectsSystem _statusEffects = default!;
[UISystemDependency] private readonly DamageableSystem _damageable = default!;
private Overlays.DamageOverlay _overlay = default!;
public override void Initialize()
@@ -90,6 +92,7 @@ public sealed class DamageOverlayUiController : UIController
return; //this entity intentionally has no overlays
}
var damagePerGroup = _damageable.GetDamagePerGroup((entity, damageable));
var critThreshold = foundThreshold.Value;
_overlay.State = mobState.CurrentState;
@@ -104,7 +107,8 @@ public sealed class DamageOverlayUiController : UIController
{
foreach (var painDamageType in damageable.PainDamageGroups)
{
damageable.DamagePerGroup.TryGetValue(painDamageType, out var painDamage);
damagePerGroup.TryGetValue(painDamageType, out var painDamage);
painLevel += painDamage;
}
_overlay.PainLevel = FixedPoint2.Min(1f, painLevel / critThreshold).Float();
@@ -115,7 +119,7 @@ public sealed class DamageOverlayUiController : UIController
}
}
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
if (damagePerGroup.TryGetValue("Airloss", out var oxyDamage))
{
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
}
@@ -127,7 +131,7 @@ public sealed class DamageOverlayUiController : UIController
case MobState.Critical:
{
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
FixedPoint2.Max(0.0, _damageable.GetTotalDamage((entity, damageable))), out var critLevel))
return;
_overlay.CritLevel = critLevel.Value.Float();
@@ -89,7 +89,7 @@ namespace Content.IntegrationTests.Tests.Commands
Assert.That(mobStateSystem.IsDead(human, mobState), Is.False);
Assert.That(mobStateSystem.IsIncapacitated(human, mobState), Is.False);
Assert.That(damageable.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(damSystem.GetTotalDamage((human, damageable)), Is.EqualTo(FixedPoint2.Zero));
});
});
await pair.CleanReturnAsync();
@@ -138,7 +138,6 @@ public sealed class SuicideCommandTests
MindComponent mindComponent = default;
MobStateComponent mobStateComp = default;
MobThresholdsComponent mobThresholdsComp = default;
DamageableComponent damageableComp = default;
await server.WaitPost(() =>
{
if (mind != null)
@@ -146,7 +145,6 @@ public sealed class SuicideCommandTests
mobStateComp = entManager.GetComponent<MobStateComponent>(player);
mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
damageableComp = entManager.GetComponent<DamageableComponent>(player);
var slashProto = protoMan.Index(DamageType);
damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)));
@@ -165,7 +163,7 @@ public sealed class SuicideCommandTests
Assert.That(mobStateSystem.IsDead(player, mobStateComp));
Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
!ghostComp.CanReturnToBody);
Assert.That(damageableComp.Damage.GetTotal(), Is.EqualTo(lethalDamageThreshold));
Assert.That(damageableSystem.GetTotalDamage(player), Is.EqualTo(lethalDamageThreshold));
});
});
@@ -291,7 +289,7 @@ public sealed class SuicideCommandTests
Assert.That(mobStateSystem.IsDead(player, mobStateComp));
Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
!ghostComp.CanReturnToBody);
Assert.That(damageableComp.Damage.DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold));
Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold));
});
});
@@ -366,7 +364,7 @@ public sealed class SuicideCommandTests
Assert.That(mobStateSystem.IsDead(player, mobStateComp));
Assert.That(entManager.TryGetComponent<GhostComponent>(mindComponent.CurrentEntity, out var ghostComp) &&
!ghostComp.CanReturnToBody);
Assert.That(damageableComp.Damage.DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold / 2));
Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold / 2));
});
});
@@ -19,17 +19,16 @@ public sealed class WindowRepair : InteractionTest
// Damage the entity.
var sys = SEntMan.System<DamageableSystem>();
var comp = Comp<DamageableComponent>();
var damageType = Server.ProtoMan.Index(BluntDamageType);
var damage = new DamageSpecifier(damageType, FixedPoint2.New(10));
Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero));
Assert.That(sys.GetTotalDamage(STarget.Value), Is.EqualTo(FixedPoint2.Zero));
await Server.WaitPost(() => sys.TryChangeDamage(SEntMan.GetEntity(Target).Value, damage, ignoreResistances: true));
await RunTicks(5);
Assert.That(comp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero));
Assert.That(sys.GetTotalDamage(STarget.Value), Is.GreaterThan(FixedPoint2.Zero));
// Repair the entity
await InteractUsing(Weld);
Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero));
Assert.That(sys.GetTotalDamage(STarget.Value), Is.EqualTo(FixedPoint2.Zero));
// Validate that we can still deconstruct the entity (i.e., that welding deconstruction is not blocked).
await Interact(
@@ -147,6 +147,7 @@ namespace Content.IntegrationTests.Tests.Damageable
await server.WaitAssertion(() =>
{
var uid = sDamageableEntity;
var ent = new Entity<DamageableComponent>(uid, sDamageableComponent);
// Check that damage is evenly distributed over a group if its a nice multiple
var types = group3.DamageTypes;
@@ -157,11 +158,11 @@ namespace Content.IntegrationTests.Tests.Damageable
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(damageToDeal));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(damageToDeal));
Assert.That(sDamageableSystem.GetDamagePerGroup(ent)[group3.ID], Is.EqualTo(damageToDeal));
foreach (var type in types)
{
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict.TryGetValue(type, out typeDamage));
Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count));
}
});
@@ -171,11 +172,11 @@ namespace Content.IntegrationTests.Tests.Damageable
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetDamagePerGroup(ent)[group3.ID], Is.EqualTo(FixedPoint2.Zero));
foreach (var type in types)
{
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict.TryGetValue(type, out typeDamage));
Assert.That(typeDamage, Is.EqualTo(FixedPoint2.Zero));
}
});
@@ -190,11 +191,11 @@ namespace Content.IntegrationTests.Tests.Damageable
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(14)));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.New(14)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(FixedPoint2.New(4.66f)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(FixedPoint2.New(4.67f)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(FixedPoint2.New(4.67f)));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.New(14)));
Assert.That(sDamageableSystem.GetDamagePerGroup(ent)[group3.ID], Is.EqualTo(FixedPoint2.New(14)));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict[type3a.ID], Is.EqualTo(FixedPoint2.New(4.66f)));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict[type3b.ID], Is.EqualTo(FixedPoint2.New(4.67f)));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict[type3c.ID], Is.EqualTo(FixedPoint2.New(4.67f)));
});
// Heal
@@ -202,36 +203,36 @@ namespace Content.IntegrationTests.Tests.Damageable
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetDamagePerGroup(ent)[group3.ID], Is.EqualTo(FixedPoint2.Zero));
foreach (var type in types)
{
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict.TryGetValue(type, out typeDamage));
Assert.That(typeDamage, Is.EqualTo(FixedPoint2.Zero));
}
// Test that unsupported groups return false when setting/getting damage (and don't change damage)
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
});
damage = new DamageSpecifier(group1, FixedPoint2.New(10)) + new DamageSpecifier(type2b, FixedPoint2.New(10));
sDamageableSystem.ChangeDamage(uid, damage, true);
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.DamagePerGroup.TryGetValue(group1.ID, out _), Is.False);
Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type1.ID, out typeDamage), Is.False);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetDamagePerGroup(ent).TryGetValue(group1.ID, out _), Is.False);
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict.TryGetValue(type1.ID, out typeDamage), Is.False);
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
});
// Test SetAll and ClearAll function
sDamageableSystem.SetAllDamage((sDamageableEntity, sDamageableComponent), 10);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(10 * sDamageableComponent.Damage.DamageDict.Count)));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.New(10 * sDamageableSystem.GetAllDamage(uid).DamageDict.Count)));
sDamageableSystem.SetAllDamage((sDamageableEntity, sDamageableComponent), 0);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
sDamageableSystem.SetAllDamage((sDamageableEntity, sDamageableComponent), 10);
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(10 * sDamageableComponent.Damage.DamageDict.Count)));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.New(10 * sDamageableSystem.GetAllDamage(uid).DamageDict.Count)));
sDamageableSystem.ClearAllDamage((sDamageableEntity, sDamageableComponent));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
// Test 'wasted' healing
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(type3a, 5));
@@ -240,18 +241,18 @@ namespace Content.IntegrationTests.Tests.Damageable
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(FixedPoint2.New(1.34)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(FixedPoint2.New(3.33)));
Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(FixedPoint2.New(0)));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict[type3a.ID], Is.EqualTo(FixedPoint2.New(1.34)));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict[type3b.ID], Is.EqualTo(FixedPoint2.New(3.33)));
Assert.That(sDamageableSystem.GetAllDamage(uid).DamageDict[type3c.ID], Is.EqualTo(FixedPoint2.New(0)));
});
// Test Over-Healing
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
// Test that if no health change occurred, returns false
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -100));
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero));
});
await pair.CleanReturnAsync();
}
@@ -117,7 +117,7 @@ namespace Content.IntegrationTests.Tests.Destructible
sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage * -10);
Assert.Multiple(() =>
{
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(20)));
Assert.That(sDamageableSystem.GetTotalDamage(sDestructibleEntity), Is.EqualTo(FixedPoint2.New(20)));
// No new thresholds reached, healing should not trigger it
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
@@ -195,7 +195,7 @@ namespace Content.IntegrationTests.Tests.Destructible
Assert.Multiple(() =>
{
// Check that the total damage matches
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50)));
Assert.That(sDamageableSystem.GetTotalDamage(sDestructibleEntity), Is.EqualTo(FixedPoint2.New(50)));
// Both thresholds should have triggered
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Exactly(2).Items);
@@ -252,7 +252,7 @@ namespace Content.IntegrationTests.Tests.Destructible
sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent));
// Check that the entity has 0 damage
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(sDamageableSystem.GetTotalDamage(sDestructibleEntity), Is.EqualTo(FixedPoint2.Zero));
// Set both thresholds to only trigger once
foreach (var destructibleThreshold in sDestructibleComponent.Thresholds)
@@ -267,7 +267,7 @@ namespace Content.IntegrationTests.Tests.Destructible
Assert.Multiple(() =>
{
// Check that the total damage matches
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50)));
Assert.That(sDamageableSystem.GetTotalDamage(sDestructibleEntity), Is.EqualTo(FixedPoint2.New(50)));
// No thresholds should have triggered as they were already triggered before, and they are set to only trigger once
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
@@ -283,7 +283,7 @@ namespace Content.IntegrationTests.Tests.Destructible
Assert.Multiple(() =>
{
// Check that the total damage matches
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50)));
Assert.That(sDamageableSystem.GetTotalDamage(sDestructibleEntity), Is.EqualTo(FixedPoint2.New(50)));
// They shouldn't have been triggered by changing TriggersOnce
Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty);
@@ -11,6 +11,7 @@ using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
@@ -58,6 +59,7 @@ public sealed class NukeOpsTest
var invSys = server.System<InventorySystem>();
var factionSys = server.System<NpcFactionSystem>();
var roundEndSys = server.System<RoundEndSystem>();
var damageSys = server.System<DamageableSystem>();
server.CfgMan.SetCVar(CCVars.GridFill, true);
@@ -231,12 +233,11 @@ public sealed class NukeOpsTest
var totalSeconds = 30;
var totalTicks = (int)Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
var increment = 5;
var damage = entMan.GetComponent<DamageableComponent>(player);
for (var tick = 0; tick < totalTicks; tick += increment)
{
await pair.RunTicksSync(increment);
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
Assert.That(damageSys.GetTotalDamage(player), Is.EqualTo(FixedPoint2.Zero));
}
}
@@ -47,7 +47,7 @@ public sealed class DefibrillatorTest : InteractionTest
Assert.Multiple(() =>
{
Assert.That(targetMobState.CurrentState, Is.EqualTo(MobState.Alive), "Target mob was not alive when spawned.");
Assert.That(targetDamageable.TotalDamage, Is.EqualTo(FixedPoint2.Zero), "Target mob was damaged when spawned.");
Assert.That(damageableSystem.GetTotalDamage(STarget!.Value), Is.EqualTo(FixedPoint2.Zero), "Target mob was damaged when spawned.");
});
// Get the damage needed to kill or crit the target.
@@ -64,7 +64,7 @@ public sealed class DefibrillatorTest : InteractionTest
Assert.Multiple(() =>
{
Assert.That(targetMobState.CurrentState, Is.EqualTo(MobState.Dead), "Target mob did not die from deadly damage amount.");
Assert.That(targetDamageable.TotalDamage, Is.EqualTo(deathThreshold), "Target mob had the wrong total damage amount after being killed.");
Assert.That(damageableSystem.GetTotalDamage(STarget!.Value), Is.EqualTo(deathThreshold), "Target mob had the wrong total damage amount after being killed.");
});
// Spawn a defib and activate it.
@@ -82,7 +82,7 @@ public sealed class DefibrillatorTest : InteractionTest
Assert.Multiple(() =>
{
Assert.That(targetMobState.CurrentState, Is.EqualTo(MobState.Dead), "Target mob was revived despite being over the death damage threshold.");
Assert.That(targetDamageable.TotalDamage, Is.GreaterThan(deathThreshold), "Target mob did not take damage from being defibrillated.");
Assert.That(damageableSystem.GetTotalDamage(STarget!.Value), Is.GreaterThan(deathThreshold), "Target mob did not take damage from being defibrillated.");
});
// Set the damage halfway between the crit and death thresholds so that the target can be revived.
@@ -1,6 +1,7 @@
using Content.IntegrationTests.Tests.Movement;
using Content.Server.NPC.HTN;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Item.ItemToggle;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Mobs;
@@ -44,9 +45,8 @@ public sealed class MousetrapMouseMoveOverTest : MovementTest
// The mouse is spawned by the test before the atmosphere is added, so it has some barotrauma damage already
// TODO: fix this since it can have an impact on integration tests
Assert.That(SEntMan.TryGetComponent<DamageableComponent>(SPlayer, out var damageComp),
$"Player does not have a DamageableComponent.");
var startingDamage = damageComp.TotalDamage;
var damageSystem = Server.System<DamageableSystem>();
var startingDamage = damageSystem.GetTotalDamage(SPlayer);
Assert.That(SEntMan.TryGetComponent<MobStateComponent>(SPlayer, out var mouseMobStateComp),
$"{MouseProtoId} does not have a MobStateComponent.");
@@ -58,7 +58,7 @@ public sealed class MousetrapMouseMoveOverTest : MovementTest
Assert.That(Delta(), Is.LessThan(0.5), "Mouse did not move over mousetrap.");
// Walking over an inactive trap does not trigger it
Assert.That(damageComp.TotalDamage, Is.LessThanOrEqualTo(startingDamage), "Mouse took damage from inactive trap!");
Assert.That(damageSystem.GetTotalDamage(SPlayer), Is.LessThanOrEqualTo(startingDamage), "Mouse took damage from inactive trap!");
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap was activated.");
// Activate the trap
@@ -72,7 +72,7 @@ public sealed class MousetrapMouseMoveOverTest : MovementTest
Assert.That(Delta(), Is.LessThan(0.1), "Mouse moved past active mousetrap.");
// Walking over an active trap triggers it
Assert.That(damageComp.TotalDamage, Is.GreaterThan(startingDamage), "Mouse did not take damage from active trap!");
Assert.That(damageSystem.GetTotalDamage(SPlayer), Is.GreaterThan(startingDamage), "Mouse did not take damage from active trap!");
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap was not deactivated after triggering.");
Assert.That(mouseMobStateComp.CurrentState, Is.EqualTo(MobState.Dead), "Mouse was not killed by trap.");
}
@@ -117,9 +117,8 @@ public sealed class MousetrapHumanMoveOverTest : MovementTest
Assert.That(itemToggleSystem.TrySetActive(STarget.Value, true), "Could not activate the mouse trap.");
});
Assert.That(SEntMan.TryGetComponent<DamageableComponent>(SPlayer, out var damageComp),
$"Player does not have a DamageableComponent.");
var startingDamage = damageComp.TotalDamage;
var damageSystem = Server.System<DamageableSystem>();
var startingDamage = damageSystem.GetTotalDamage(SPlayer);
// Move player over the trap
await Move(DirectionFlag.East, 0.5f);
@@ -127,7 +126,7 @@ public sealed class MousetrapHumanMoveOverTest : MovementTest
Assert.That(Delta(), Is.LessThan(0.5), "Player did not move over mousetrap.");
// Walking over the trap without shoes activates it
Assert.That(damageComp.TotalDamage, Is.GreaterThan(startingDamage), "Player did not take damage.");
Assert.That(damageSystem.GetTotalDamage(SPlayer), Is.GreaterThan(startingDamage), "Player did not take damage.");
Assert.That(itemToggleComp.Activated, Is.False, "Mousetrap was not deactivated after triggering.");
// Reactivate the trap
@@ -135,7 +134,7 @@ public sealed class MousetrapHumanMoveOverTest : MovementTest
{
Assert.That(itemToggleSystem.TrySetActive(STarget.Value, true), "Could not activate the mouse trap.");
});
var afterStepDamage = damageComp.TotalDamage;
var afterStepDamage = damageSystem.GetTotalDamage(SPlayer);
// Give the player some shoes
await PlaceInHands(ShoesProtoId);
@@ -147,7 +146,7 @@ public sealed class MousetrapHumanMoveOverTest : MovementTest
Assert.That(Delta(), Is.GreaterThan(0.5), "Player did not move back over mousetrap.");
// Walking over the trap with shoes on does not activate it
Assert.That(damageComp.TotalDamage, Is.LessThanOrEqualTo(afterStepDamage), "Player took damage from trap!");
Assert.That(damageSystem.GetTotalDamage(SPlayer), Is.LessThanOrEqualTo(afterStepDamage), "Player took damage from trap!");
Assert.That(itemToggleComp.Activated, "Mousetrap was deactivated despite the player being protected by shoes.");
}
}
@@ -196,14 +196,14 @@ public sealed class VendingInteractionTest : InteractionTest
private async Task BreakVendor()
{
var damageableSys = SEntMan.System<DamageableSystem>();
Assert.That(TryComp<DamageableComponent>(out var damageableComp), $"{VendingMachineProtoId} does not have DamageableComponent.");
Assert.That(damageableComp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero), $"{VendingMachineProtoId} started with unexpected damage.");
Assert.That(HasComp<DamageableComponent>(), $"{VendingMachineProtoId} does not have DamageableComponent.");
Assert.That(damageableSys.GetAllDamage(STarget!.Value).GetTotal(), Is.EqualTo(FixedPoint2.Zero), $"{VendingMachineProtoId} started with unexpected damage.");
// Damage the vending machine to the point that it breaks
var damageType = ProtoMan.Index(TestDamageType);
var damage = new DamageSpecifier(damageType, FixedPoint2.New(100));
await Server.WaitPost(() => damageableSys.TryChangeDamage(SEntMan.GetEntity(Target).Value, damage, ignoreResistances: true));
await RunTicks(5);
Assert.That(damageableComp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero), $"{VendingMachineProtoId} did not take damage.");
Assert.That(damageableSys.GetAllDamage(STarget!.Value).GetTotal(), Is.GreaterThan(FixedPoint2.Zero), $"{VendingMachineProtoId} did not take damage.");
}
}
@@ -1,5 +1,7 @@
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components;
@@ -17,6 +19,7 @@ public sealed class WeaponTests : InteractionTest
public async Task GunRequiresWieldTest()
{
var gunSystem = SEntMan.System<SharedGunSystem>();
var damageSystem = SEntMan.System<DamageableSystem>();
await AddAtmosphere(); // prevent the Urist from suffocating
@@ -44,8 +47,8 @@ public sealed class WeaponTests : InteractionTest
Assert.That(updatedAmmo,
Is.EqualTo(startAmmo),
"Mosin discharged ammo when the weapon should not have fired!");
Assert.That(damageComp.TotalDamage.Value,
Is.EqualTo(0),
Assert.That(damageSystem.GetTotalDamage(ToServer(urist)),
Is.EqualTo(FixedPoint2.Zero),
"Urist took damage when the weapon should not have fired!");
await UseInHand();
@@ -56,8 +59,8 @@ public sealed class WeaponTests : InteractionTest
updatedAmmo = gunSystem.GetAmmoCount(mosinEnt);
Assert.That(updatedAmmo, Is.EqualTo(startAmmo - 1), "Mosin failed to discharge appropriate amount of ammo!");
Assert.That(damageComp.TotalDamage.Value,
Is.GreaterThan(0),
Assert.That(damageSystem.GetTotalDamage(ToServer(urist)),
Is.GreaterThan(FixedPoint2.Zero),
"Mosin was fired but urist sustained no damage!");
}
}
@@ -95,6 +95,7 @@ public sealed partial class AdminVerbSystem
[Dependency] private readonly SuperBonkSystem _superBonkSystem = default!;
[Dependency] private readonly SlipperySystem _slipperySystem = default!;
[Dependency] private readonly GibbingSystem _gibbing = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
private readonly EntProtoId _actionViewLawsProtoId = "ActionViewLaws";
private readonly ProtoId<SiliconLawsetPrototype> _crewsimovLawset = "Crewsimov";
@@ -230,6 +231,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Hands/Gloves/Color/yellow.rsi"), "icon"),
Act = () =>
{
var totalDamage = _damageable.GetTotalDamage((args.Target, damageable));
int damageToDeal;
if (!_mobThresholdSystem.TryGetThresholdForState(args.Target, MobState.Critical, out var criticalThreshold))
{
@@ -237,11 +239,11 @@ public sealed partial class AdminVerbSystem
if (!_mobThresholdSystem.TryGetThresholdForState(args.Target, MobState.Dead,
out var deadThreshold))
return;// whelp.
damageToDeal = deadThreshold.Value.Int() - (int)damageable.TotalDamage;
damageToDeal = deadThreshold.Value.Int() - (int)totalDamage;
}
else
{
damageToDeal = criticalThreshold.Value.Int() - (int)damageable.TotalDamage;
damageToDeal = criticalThreshold.Value.Int() - (int)totalDamage;
}
if (damageToDeal <= 0)
@@ -211,9 +211,10 @@ namespace Content.Server.Atmos.EntitySystems
while (enumerator.MoveNext(out var uid, out var barotrauma, out var damageable))
{
var totalDamage = FixedPoint2.Zero;
var damageSpecifier = _damageableSystem.GetAllDamage((uid, damageable));
foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict)
{
if (!damageable.Damage.DamageDict.TryGetValue(barotraumaDamageType, out var damage))
if (!damageSpecifier.DamageDict.TryGetValue(barotraumaDamageType, out var damage))
continue;
totalDamage += damage;
}
+3 -1
View File
@@ -30,6 +30,7 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage.Systems;
namespace Content.Server.Cloning;
@@ -55,6 +56,7 @@ public sealed class CloningPodSystem : EntitySystem
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly CloningSystem _cloning = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public readonly Dictionary<MindComponent, EntityUid> ClonesWaitingForMind = new();
public readonly ProtoId<CloningSettingsPrototype> SettingsId = "CloningPod";
@@ -180,7 +182,7 @@ public sealed class CloningPodSystem : EntitySystem
// genetic damage checks
if (TryComp<DamageableComponent>(bodyToClone, out var damageable) &&
damageable.Damage.DamageDict.TryGetValue("Cellular", out var cellularDmg))
_damageable.GetAllDamage((bodyToClone, damageable)).DamageDict.TryGetValue("Cellular", out var cellularDmg))
{
var chance = Math.Clamp((float)(cellularDmg / 100), 0, 1);
chance *= failChanceModifier;
@@ -1,87 +0,0 @@
using Content.Server.Destructible;
using Content.Shared.Construction;
using Content.Shared.Damage.Components;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
namespace Content.Server.Construction.Conditions;
/// <summary>
/// Requires that the structure has at least some amount of health
/// </summary>
[DataDefinition]
public sealed partial class MinHealth : IGraphCondition
{
/// <summary>
/// If ByProportion is true, Threshold is a value less than or equal to 1, but more than 0,
/// which is compared to the percent of health remaining in the structure.
/// Else, Threshold is any positive value with at most 2 decimal points of percision,
/// which is compared to the current health of the structure.
/// </summary>
[DataField]
public FixedPoint2 Threshold = 1;
[DataField]
public bool ByProportion = false;
[DataField]
public bool IncludeEquals = true;
public bool Condition(EntityUid uid, IEntityManager entMan)
{
if (!entMan.TryGetComponent(uid, out DestructibleComponent? destructibleComp) ||
!entMan.TryGetComponent(uid, out DamageableComponent? damageComp))
{
return false;
}
var destructionSys = entMan.System<DestructibleSystem>();
var maxHealth = destructionSys.DestroyedAt(uid, destructibleComp);
var curHealth = maxHealth - damageComp.TotalDamage;
var proportionHealth = curHealth / maxHealth;
if (IncludeEquals)
{
if (ByProportion)
{
return proportionHealth >= Threshold;
}
else
{
return curHealth >= Threshold;
}
}
else
{
if (ByProportion)
{
return proportionHealth > Threshold;
}
else
{
return curHealth > Threshold;
}
}
}
public bool DoExamine(ExaminedEvent args)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var entity = args.Examined;
if (Condition(entity, entMan))
{
return false;
}
args.PushMarkup(Loc.GetString("construction-examine-condition-low-health"));
return true;
}
public IEnumerable<ConstructionGuideEntry> GenerateGuideEntry()
{
yield return new ConstructionGuideEntry()
{
Localization = "construction-step-condition-low-health"
};
}
}
@@ -1,6 +1,7 @@
using Content.Server.Damage.Components;
using Content.Server.Destructible;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.Rounding;
using Robust.Shared.Prototypes;
@@ -11,6 +12,7 @@ public sealed class ExaminableDamageSystem : EntitySystem
{
[Dependency] private readonly DestructibleSystem _destructible = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -39,7 +41,7 @@ public sealed class ExaminableDamageSystem : EntitySystem
if (!TryComp<DamageableComponent>(ent, out var damageable))
return 0;
var damage = damageable.TotalDamage;
var damage = _damageable.GetTotalDamage((ent, damageable));
var damageThreshold = _destructible.DestroyedAt(ent);
if (damageThreshold == 0)
@@ -245,7 +245,7 @@ public sealed partial class ExplosionSystem
var damagePerIntensity = FixedPoint2.Zero;
foreach (var (type, value) in explosionType.DamagePerIntensity.DamageDict)
{
if (!damageable.Damage.DamageDict.ContainsKey(type))
if (_damageableSystem.CanBeDamagedBy((uid, damageable), type))
continue;
// TODO EXPLOSION SYSTEM
@@ -260,7 +260,7 @@ public sealed partial class ExplosionSystem
}
var toleranceValue = damagePerIntensity > 0
? (float) ((totalDamageTarget - damageable.TotalDamage) / damagePerIntensity)
? (float) ((totalDamageTarget - _damageableSystem.GetTotalDamage((uid, damageable))) / damagePerIntensity)
: ToleranceValues.Invulnerable;
explosionTolerance[index] = toleranceValue;
+2 -1
View File
@@ -590,7 +590,8 @@ namespace Content.Server.Ghost
&& TryComp<MobThresholdsComponent>(playerEntity, out var thresholds))
{
var playerDeadThreshold = _mobThresholdSystem.GetThresholdForState(playerEntity.Value, MobState.Dead, thresholds);
dealtDamage = playerDeadThreshold - damageable.TotalDamage;
dealtDamage = playerDeadThreshold -
_damageable.GetTotalDamage((playerEntity.Value, damageable));
}
DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage);
+1 -1
View File
@@ -258,7 +258,7 @@ public sealed partial class MechSystem : SharedMechSystem
private void OnDamageChanged(EntityUid uid, MechComponent component, DamageChangedEvent args)
{
var integrity = component.MaxIntegrity - args.Damageable.TotalDamage;
var integrity = component.MaxIntegrity - _damageable.GetTotalDamage((uid, args.Damageable));
SetIntegrity(uid, integrity, component);
if (args.DamageIncreased &&
+4 -1
View File
@@ -6,6 +6,7 @@ using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.NodeGroups;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Content.Shared.Damage.Systems;
using Content.Shared.Medical.Cryogenics;
namespace Content.Server.Medical;
@@ -16,6 +17,7 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
[Dependency] private readonly GasAnalyzerSystem _gasAnalyzerSystem = default!;
[Dependency] private readonly HealthAnalyzerSystem _healthAnalyzerSystem = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
@@ -55,11 +57,12 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
var injecting = GetInjectingReagents(entity);
var health = _healthAnalyzerSystem.GetHealthAnalyzerUiState(patient);
health.ScanMode = true;
var hasDamage = patient is null ? false : _damageable.GetTotalDamage(patient.Value) > 0;
UI.ServerSendUiMessage(
entity.Owner,
CryoPodUiKey.Key,
new CryoPodUserMessage(gasMix, health, beakerCapacity, beaker, injecting)
new CryoPodUserMessage(gasMix, health, beakerCapacity, beaker, injecting, hasDamage)
);
}
+3 -2
View File
@@ -46,12 +46,13 @@ public sealed class MeteorSystem : EntitySystem
{
threshold = FixedPoint2.MaxValue;
}
var otherEntDamage = CompOrNull<DamageableComponent>(args.OtherEntity)?.TotalDamage ?? FixedPoint2.Zero;
var otherEntDamage = _damageable.GetTotalDamage(args.OtherEntity);
// account for the damage that the other entity has already taken: don't overkill
threshold -= otherEntDamage;
// The max amount of damage our meteor can take before breaking.
var maxMeteorDamage = _destructible.DestroyedAt(uid) - CompOrNull<DamageableComponent>(uid)?.TotalDamage ?? FixedPoint2.Zero;
var maxMeteorDamage = _destructible.DestroyedAt(uid) - _damageable.GetTotalDamage(uid);
// Cap damage so we don't overkill the meteor
var trueDamage = FixedPoint2.Min(maxMeteorDamage, threshold);
@@ -4,10 +4,12 @@ using Content.Shared.NPC.Components;
using Content.Server.NPC.Pathfinding;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Silicons.Bots;
using Content.Shared.Emag.Components;
using Content.Shared.FixedPoint;
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific;
@@ -16,6 +18,7 @@ public sealed partial class PickNearbyInjectableOperator : HTNOperator
[Dependency] private readonly IEntityManager _entManager = default!;
private MedibotSystem _medibot = default!;
private PathfindingSystem _pathfinding = default!;
private DamageableSystem _damageable = default!;
private EntityQuery<DamageableComponent> _damageQuery = default!;
private EntityQuery<InjectableSolutionComponent> _injectQuery = default!;
@@ -42,6 +45,7 @@ public sealed partial class PickNearbyInjectableOperator : HTNOperator
base.Initialize(sysManager);
_medibot = sysManager.GetEntitySystem<MedibotSystem>();
_pathfinding = sysManager.GetEntitySystem<PathfindingSystem>();
_damageable = sysManager.GetEntitySystem<DamageableSystem>();
_damageQuery = _entManager.GetEntityQuery<DamageableComponent>();
_injectQuery = _entManager.GetEntityQuery<InjectableSolutionComponent>();
@@ -79,7 +83,7 @@ public sealed partial class PickNearbyInjectableOperator : HTNOperator
// Only go towards a target if the bot can actually help them or if the medibot is emagged
// note: this and the actual injecting don't check for specific damage types so for example,
// radiation damage will trigger injection but the tricordrazine won't heal it.
if (!_emaggedQuery.HasComponent(entity) && !treatment.IsValid(damage.TotalDamage))
if (!_emaggedQuery.HasComponent(entity) && _damageable.GetTotalDamage((entity, damage)) == FixedPoint2.Zero)
continue;
//Needed to make sure it doesn't sometimes stop right outside it's interaction range
@@ -29,6 +29,7 @@ using Robust.Shared.Utility;
using Content.Shared.Atmos.Components;
using System.Linq;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Temperature.Components;
namespace Content.Server.NPC.Systems;
@@ -54,6 +55,7 @@ public sealed class NPCUtilitySystem : EntitySystem
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
[Dependency] private readonly TurretTargetSettingsSystem _turretTargetSettings = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
private EntityQuery<PuddleComponent> _puddleQuery;
private EntityQuery<TransformComponent> _xformQuery;
@@ -304,9 +306,10 @@ public sealed class NPCUtilitySystem : EntitySystem
{
if (!TryComp(targetUid, out DamageableComponent? damage))
return 0f;
if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, damage.TotalDamage, out var percentage))
var totalDamage = _damageable.GetTotalDamage((targetUid, damage));
if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, totalDamage, out var percentage))
return Math.Clamp((float)(1 - percentage), 0f, 1f);
if (_thresholdSystem.TryGetIncapPercentage(targetUid, damage.TotalDamage, out var incapPercentage))
if (_thresholdSystem.TryGetIncapPercentage(targetUid, totalDamage, out var incapPercentage))
return Math.Clamp((float)(1 - incapPercentage), 0f, 1f);
return 0f;
}
@@ -53,7 +53,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
var damageRequired = _destructibleSystem.DestroyedAt(target);
if (TryComp<DamageableComponent>(target, out var damageableComponent))
{
damageRequired -= damageableComponent.TotalDamage;
damageRequired -= _damageableSystem.GetTotalDamage((target, damageableComponent));
damageRequired = FixedPoint2.Max(damageRequired, FixedPoint2.Zero);
}
var deleted = Deleted(target);
@@ -179,7 +179,7 @@ public sealed partial class BorgSystem
return 1;
}
return 1 - ((FixedPoint2)(damageable.TotalDamage / threshold)).Float();
return 1 - ((FixedPoint2)(_damageable.GetTotalDamage((uid, damageable)) / threshold)).Float();
}
/// <summary>
@@ -1,6 +1,7 @@
using Content.Server.Administration.Managers;
using Content.Server.DeviceNetwork.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Damage.Systems;
using Content.Shared.Emag.Systems;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
@@ -31,6 +32,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public static readonly ProtoId<JobPrototype> BorgJobId = "Borg";
@@ -120,7 +120,7 @@ public sealed class StationAiSystem : SharedStationAiSystem
// Set the new AI brain to the 'rebooting' state
if (TryComp<StationAiCustomizationComponent>(aiBrain, out var customization))
SetStationAiState((aiBrain, customization), StationAiState.Rebooting);
}
// Delete the new AI brain if it cannot be inserted into the core
@@ -252,7 +252,7 @@ public sealed class StationAiSystem : SharedStationAiSystem
accent.OverrideChargeLevel = _battery.GetChargeLevel((ent.Owner, battery));
if (TryComp<DamageableComponent>(ent, out var damageable))
accent.OverrideTotalDamage = damageable.TotalDamage;
accent.OverrideTotalDamage = _damageable.GetTotalDamage((ent, damageable));
if (TryComp<DestructibleComponent>(ent, out var destructible))
accent.DamageAtMaxCorruption = _destructible.DestroyedAt(ent, destructible);
@@ -299,7 +299,7 @@ public sealed class StationAiSystem : SharedStationAiSystem
if (!_proto.TryIndex(_damageAlert, out var proto))
return;
var damagePercent = damageable.TotalDamage / _destructible.DestroyedAt(ent, destructible);
var damagePercent = _damageable.GetTotalDamage((ent, damageable)) / _destructible.DestroyedAt(ent, destructible);
var damageLevel = Math.Round(damagePercent.Float() * proto.MaxSeverity);
_alerts.ShowAlert(held.Value, _damageAlert, (short)Math.Clamp(damageLevel, 0, proto.MaxSeverity));
@@ -2,6 +2,7 @@
using Content.Server.Destructible;
using Content.Shared.Speech.Components;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Power.EntitySystems;
using Content.Shared.PowerCell;
@@ -16,6 +17,7 @@ public sealed class DamagedSiliconAccentSystem : EntitySystem
[Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly DestructibleSystem _destructibleSystem = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -52,7 +54,7 @@ public sealed class DamagedSiliconAccentSystem : EntitySystem
}
else if (TryComp<DamageableComponent>(uid, out var damageable))
{
damage = damageable.TotalDamage;
damage = _damageable.GetTotalDamage((uid, damageable));
}
// Corrupt due to damage (drop, repeat, replace with symbols)
args.Message = CorruptDamage(args.Message, damage, ent);
+4 -3
View File
@@ -30,7 +30,7 @@ public sealed class KudzuSystem : EntitySystem
{
// Every time we take any damage, we reduce growth depending on all damage over the growth impact
// So the kudzu gets slower growing the more it is hurt.
var growthDamage = (int) (args.Damageable.TotalDamage / component.GrowthHealth);
var growthDamage = (int) (_damageable.GetTotalDamage((uid, args.Damageable)) / component.GrowthHealth);
if (growthDamage > 0)
{
if (!EnsureComp<GrowingKudzuComponent>(uid, out _))
@@ -118,14 +118,15 @@ public sealed class KudzuSystem : EntitySystem
if (damageableQuery.TryGetComponent(uid, out var damage))
{
if (damage.TotalDamage > 1.0)
var totalDamage = _damageable.GetTotalDamage((uid, damage));
if (totalDamage > 1.0)
{
if (kudzu.DamageRecovery != null)
{
// This kudzu features healing, so Gradually heal
_damageable.TryChangeDamage(uid, kudzu.DamageRecovery, true);
}
if (damage.TotalDamage >= kudzu.GrowthBlock)
if (totalDamage >= kudzu.GrowthBlock)
{
// Don't grow when quite damaged
if (_robustRandom.Prob(0.95f))
@@ -1,3 +1,4 @@
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
@@ -53,11 +54,12 @@ public sealed partial class BlockingSystem
return;
var blockFraction = blocking.IsBlocking ? blocking.ActiveBlockFraction : blocking.PassiveBlockFraction;
var modifier = blocking.IsBlocking ? blocking.ActiveBlockDamageModifier : blocking.PassiveBlockDamageModifer;
blockFraction = Math.Clamp(blockFraction, 0, 1);
_damageable.TryChangeDamage((item, dmgComp), blockFraction * args.OriginalDamage);
var modify = new DamageModifierSet();
foreach (var key in dmgComp.Damage.DamageDict.Keys)
foreach (var key in modifier.Coefficients.Keys.Concat(modifier.FlatReduction.Keys))
{
modify.Coefficients.TryAdd(key, 1 - blockFraction);
}
@@ -81,12 +81,6 @@ public sealed partial class ChangelingDevourComponent : Component
[DataField, AutoNetworkedField]
public TimeSpan DevourConsumeTime = TimeSpan.FromSeconds(10);
/// <summary>
/// Damage cap that a target is allowed to be caused due to IdentityConsumption
/// </summary>
[DataField, AutoNetworkedField]
public float DevourConsumeDamageCap = 350f;
/// <summary>
/// The Currently active devour sound in the world
/// </summary>
@@ -84,16 +84,7 @@ public sealed class ChangelingDevourSystem : EntitySystem
if (target == null)
return;
if (!TryComp<DamageableComponent>(target, out var damage))
return;
foreach (var damagePoints in comp.DamagePerTick.DamageDict)
{
if (damage.Damage.DamageDict.TryGetValue(damagePoints.Key, out var val) && val > comp.DevourConsumeDamageCap)
return;
}
_damageable.ChangeDamage((target.Value, damage), comp.DamagePerTick, true, true, user);
_damageable.ChangeDamage(target.Value, comp.DamagePerTick, true, true, user);
}
/// <summary>
+2 -2
View File
@@ -29,7 +29,7 @@ public sealed class SharedSuicideSystem : EntitySystem
// Mob thresholds are sorted from alive -> crit -> dead,
// grabbing the last key will give us how much damage is needed to kill a target from zero
// The exact lethal damage amount is adjusted based on their current damage taken
var lethalAmountOfDamage = mobThresholds.Thresholds.Keys.Last() - target.Comp.TotalDamage;
var lethalAmountOfDamage = mobThresholds.Thresholds.Keys.Last() - _damageableSystem.GetTotalDamage(target.AsNullable());
var totalDamage = appliedDamageSpecifier.GetTotal();
// Removing structural because it causes issues against entities that cannot take structural damage,
@@ -56,7 +56,7 @@ public sealed class SharedSuicideSystem : EntitySystem
// Mob thresholds are sorted from alive -> crit -> dead,
// grabbing the last key will give us how much damage is needed to kill a target from zero
// The exact lethal damage amount is adjusted based on their current damage taken
var lethalAmountOfDamage = mobThresholds.Thresholds.Keys.Last() - target.Comp.TotalDamage;
var lethalAmountOfDamage = mobThresholds.Thresholds.Keys.Last() - _damageableSystem.GetTotalDamage(target.AsNullable());
// We don't want structural damage for the same reasons listed above
if (!_prototypeManager.TryIndex(damageType, out var damagePrototype) || damagePrototype.ID == "Structural")
@@ -47,6 +47,7 @@ public sealed partial class DamageableComponent : Component
/// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
/// </remarks>
[DataField]
[Access(typeof(DamageableSystem), Other = AccessPermissions.None)]
public DamageSpecifier Damage = new();
/// <summary>
@@ -56,12 +57,15 @@ public sealed partial class DamageableComponent : Component
/// Groups which have no members that are supported by this component will not be present in this
/// dictionary.
/// </remarks>
[ViewVariables] public Dictionary<string, FixedPoint2> DamagePerGroup = new();
[ViewVariables]
[Access(typeof(DamageableSystem), Other = AccessPermissions.None)]
public Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> DamagePerGroup = new();
/// <summary>
/// The sum of all damages in the DamageableComponent.
/// </summary>
[ViewVariables]
[Access(typeof(DamageableSystem), Other = AccessPermissions.None)]
public FixedPoint2 TotalDamage;
[DataField("radiationDamageTypes")]
@@ -8,7 +8,4 @@ public sealed partial class GodmodeComponent : Component
{
[DataField("wasMovedByPressure")]
public bool WasMovedByPressure;
[DataField("oldDamage")]
public DamageSpecifier? OldDamage = null;
}
@@ -29,12 +29,6 @@ public sealed partial class PassiveDamageComponent : Component
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float Interval = 1f;
/// <summary>
/// The maximum HP the damage will be given to. If 0, disabled.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 DamageCap = 0;
[DataField("nextDamage", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan NextDamage = TimeSpan.Zero;
}
+3 -3
View File
@@ -320,15 +320,15 @@ namespace Content.Shared.Damage
/// total of each group. If no members of a group are present in this <see cref="DamageSpecifier"/>, the
/// group is not included in the resulting dictionary.
/// </remarks>
public Dictionary<string, FixedPoint2> GetDamagePerGroup(IPrototypeManager protoManager)
public Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> GetDamagePerGroup(IPrototypeManager protoManager)
{
var dict = new Dictionary<string, FixedPoint2>();
var dict = new Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2>();
GetDamagePerGroup(protoManager, dict);
return dict;
}
/// <inheritdoc cref="GetDamagePerGroup(Robust.Shared.Prototypes.IPrototypeManager)"/>
public void GetDamagePerGroup(IPrototypeManager protoManager, Dictionary<string, FixedPoint2> dict)
public void GetDamagePerGroup(IPrototypeManager protoManager, Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> dict)
{
dict.Clear();
foreach (var group in protoManager.EnumeratePrototypes<DamageGroupPrototype>())
@@ -1,3 +1,5 @@
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Damage
@@ -14,16 +16,16 @@ namespace Content.Shared.Damage
[Serializable, NetSerializable]
public sealed class DamageVisualizerGroupData : ICloneable
{
public List<string> GroupList;
public List<ProtoId<DamageGroupPrototype>> GroupList;
public DamageVisualizerGroupData(List<string> groupList)
public DamageVisualizerGroupData(List<ProtoId<DamageGroupPrototype>> groupList)
{
GroupList = groupList;
}
public object Clone()
{
return new DamageVisualizerGroupData(new List<string>(GroupList));
return new DamageVisualizerGroupData(new List<ProtoId<DamageGroupPrototype>>(GroupList));
}
}
}
@@ -7,6 +7,7 @@ namespace Content.Shared.Damage.Systems;
public sealed class DamagePopupSystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -19,7 +20,7 @@ public sealed class DamagePopupSystem : EntitySystem
{
if (args.DamageDelta != null)
{
var damageTotal = args.Damageable.TotalDamage;
var damageTotal = _damageable.GetTotalDamage((ent, args.Damageable));
var damageDelta = args.DamageDelta.GetTotal();
var msg = ent.Comp.Type switch
@@ -301,7 +301,7 @@ public sealed partial class DamageableSystem
ProtoId<DamageGroupPrototype>? group = null)
{
// get the damage should be healed (either all or only from one group)
damage = group == null ? GetDamage(ent) : GetDamage(ent, group.Value);
damage = group == null ? GetPositiveDamage(ent) : GetPositiveDamage(ent, group.Value);
// If trying to heal more than the total damage of damageEntity just heal everything
return damage.GetTotal() > amount;
@@ -313,7 +313,7 @@ public sealed partial class DamageableSystem
/// <param name="ent">entity with damage</param>
/// <param name="group">group of damage to get values from</param>
/// <returns></returns>
public DamageSpecifier GetDamage(Entity<DamageableComponent> ent, ProtoId<DamageGroupPrototype> group)
public DamageSpecifier GetPositiveDamage(Entity<DamageableComponent> ent, ProtoId<DamageGroupPrototype> group)
{
// No damage if no group exists...
if (!_prototypeManager.Resolve(group, out var groupProto))
@@ -338,7 +338,7 @@ public sealed partial class DamageableSystem
/// </summary>
/// <param name="ent">entity with damage</param>
/// <returns></returns>
public DamageSpecifier GetDamage(Entity<DamageableComponent> ent)
public DamageSpecifier GetPositiveDamage(Entity<DamageableComponent> ent)
{
var damage = new DamageSpecifier();
damage.DamageDict.EnsureCapacity(ent.Comp.Damage.DamageDict.Count);
@@ -428,4 +428,52 @@ public sealed partial class DamageableSystem
Dirty(ent);
}
/// <summary>
/// Gets the damages currently sustained by an entity.
/// </summary>
[Obsolete("Do not rely on the ability to determine a numerically quantifiable amount of damage")]
public DamageSpecifier GetAllDamage(Entity<DamageableComponent?> ent)
{
if (!_damageableQuery.Resolve(ent, ref ent.Comp))
return new();
return ent.Comp.Damage.Clone();
}
/// <summary>
/// Gets the total amount of damage currently sustained by an entity.
/// </summary>
[Obsolete("Do not rely on the ability to determine a numerically quantifiable amount of damage")]
public FixedPoint2 GetTotalDamage(Entity<DamageableComponent?> ent)
{
if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
return FixedPoint2.Zero;
return ent.Comp.TotalDamage;
}
/// <summary>
/// Gets the total amount of damage currently sustained by an entity, indexed by damage group.
/// </summary>
[Obsolete("Do not rely on the ability to determine a numerically quantifiable amount of damage")]
public IReadOnlyDictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> GetDamagePerGroup(Entity<DamageableComponent?> ent)
{
if (!_damageableQuery.Resolve(ent, ref ent.Comp))
return new Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2>();
return ent.Comp.DamagePerGroup;
}
/// <summary>
/// Returns whether the entity can be damaged by the given type of damage
/// </summary>
[Obsolete("Do not rely on the ability to determine if an entity will be able to be damaged by something")]
public bool CanBeDamagedBy(Entity<DamageableComponent?> ent, ProtoId<DamageTypePrototype> type)
{
if (!_damageableQuery.Resolve(ent, ref ent.Comp, false))
return false;
return SupportsType(ent.Comp.DamageContainerID, type);
}
}
@@ -35,9 +35,6 @@ public sealed class PassiveDamageSystem : EntitySystem
if (comp.NextDamage > curTime)
continue;
if (comp.DamageCap != 0 && damage.TotalDamage >= comp.DamageCap)
continue;
// Set the next time they can take damage
comp.NextDamage = curTime + TimeSpan.FromSeconds(1f);
@@ -15,7 +15,6 @@ namespace Content.Shared.Damage.Systems;
public abstract class SharedGodmodeSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -69,13 +68,6 @@ public abstract class SharedGodmodeSystem : EntitySystem
public virtual void EnableGodmode(EntityUid uid, GodmodeComponent? godmode = null)
{
godmode ??= EnsureComp<GodmodeComponent>(uid);
if (TryComp<DamageableComponent>(uid, out var damageable))
{
godmode.OldDamage = new DamageSpecifier(damageable.Damage);
}
// Rejuv to cover other stuff
RaiseLocalEvent(uid, new RejuvenateEvent());
}
@@ -85,11 +77,6 @@ public abstract class SharedGodmodeSystem : EntitySystem
if (!Resolve(uid, ref godmode, false))
return;
if (godmode.OldDamage != null)
{
_damageable.SetDamage(uid, godmode.OldDamage);
}
RemComp<GodmodeComponent>(uid);
}
@@ -10,6 +10,7 @@ namespace Content.Shared.Damage.Systems;
public sealed class SlowOnDamageSystem : EntitySystem
{
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
[Dependency] private readonly DamageableSystem _damage = default!;
public override void Initialize()
{
@@ -33,12 +34,14 @@ public sealed class SlowOnDamageSystem : EntitySystem
if (!TryComp<DamageableComponent>(uid, out var damage))
return;
if (damage.TotalDamage == FixedPoint2.Zero)
var totalDamage = _damage.GetTotalDamage((uid, damage));
if (totalDamage == FixedPoint2.Zero)
return;
// Get closest threshold
FixedPoint2 closest = FixedPoint2.Zero;
var total = damage.TotalDamage;
var total = totalDamage;
foreach (var thres in component.SpeedModifierThresholds)
{
if (total >= thres.Key && thres.Key > closest)
@@ -1,7 +1,12 @@
using Content.Shared.Damage.Systems;
namespace Content.Shared.Destructible;
public abstract class SharedDestructibleSystem : EntitySystem
{
// TODO: I don't really like this but this is out of scope to re-do destructible triggers while refactoring damageable
[Dependency] public readonly DamageableSystem Damageable = default!;
/// <summary>
/// Force entity to be destroyed and deleted.
/// </summary>
@@ -28,6 +28,6 @@ public sealed partial class DamageGroupTrigger : IThresholdTrigger
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
{
return damageable.Comp.DamagePerGroup.GetValueOrDefault(DamageGroup) >= Damage;
return system.Damageable.GetDamagePerGroup(damageable.Owner).GetValueOrDefault(DamageGroup) >= Damage;
}
}
@@ -20,6 +20,6 @@ public sealed partial class DamageTrigger : IThresholdTrigger
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
{
return damageable.Comp.TotalDamage >= Damage;
return system.Damageable.GetTotalDamage(damageable.AsNullable()) >= Damage;
}
}
@@ -28,7 +28,7 @@ public sealed partial class DamageTypeTrigger : IThresholdTrigger
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
{
return damageable.Comp.Damage.DamageDict.TryGetValue(DamageType, out var damageReceived) &&
return system.Damageable.GetAllDamage(damageable.AsNullable()).DamageDict.TryGetValue(DamageType, out var damageReceived) &&
damageReceived >= Damage;
}
}
@@ -1,38 +0,0 @@
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared.EntityConditions.Conditions;
/// <summary>
/// Returns true if this entity can take damage and if its damage of a given damage group is within a specified minimum and maximum.
/// </summary>
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
public sealed partial class DamageGroupEntityConditionSystem : EntityConditionSystem<DamageableComponent, DamageGroupCondition>
{
protected override void Condition(Entity<DamageableComponent> entity, ref EntityConditionEvent<DamageGroupCondition> args)
{
var value = entity.Comp.DamagePerGroup.GetValueOrDefault(args.Condition.DamageGroup);
args.Result = value >= args.Condition.Min && value <= args.Condition.Max;
}
}
/// <inheritdoc cref="EntityCondition"/>
public sealed partial class DamageGroupCondition : EntityConditionBase<DamageGroupCondition>
{
[DataField]
public FixedPoint2 Max = FixedPoint2.MaxValue;
[DataField]
public FixedPoint2 Min = FixedPoint2.Zero;
[DataField(required: true)]
public ProtoId<DamageGroupPrototype> DamageGroup;
public override string EntityConditionGuidebookText(IPrototypeManager prototype) =>
Loc.GetString("entity-condition-guidebook-group-damage",
("max", Max == FixedPoint2.MaxValue ? int.MaxValue : Max.Float()),
("min", Min.Float()),
("type", prototype.Index(DamageGroup).LocalizedName));
}
@@ -1,39 +0,0 @@
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared.EntityConditions.Conditions;
/// <summary>
/// Returns true if this entity can take damage and if its damage of a given damage type is within a specified minimum and maximum.
/// </summary>
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
public sealed partial class DamageTypeEntityConditionSystem : EntityConditionSystem<DamageableComponent, DamageTypeCondition>
{
protected override void Condition(Entity<DamageableComponent> entity, ref EntityConditionEvent<DamageTypeCondition> args)
{
var value = entity.Comp.Damage.DamageDict.GetValueOrDefault(args.Condition.DamageType);
args.Result = value >= args.Condition.Min && value <= args.Condition.Max;
}
}
/// <inheritdoc cref="EntityCondition"/>
public sealed partial class DamageTypeCondition : EntityConditionBase<DamageTypeCondition>
{
[DataField]
public FixedPoint2 Max = FixedPoint2.MaxValue;
[DataField]
public FixedPoint2 Min = FixedPoint2.Zero;
[DataField(required: true)]
public ProtoId<DamageTypePrototype> DamageType;
public override string EntityConditionGuidebookText(IPrototypeManager prototype) =>
Loc.GetString("entity-condition-guidebook-type-damage",
("max", Max == FixedPoint2.MaxValue ? int.MaxValue : Max.Float()),
("min", Min.Float()),
("type", prototype.Index(DamageType).LocalizedName));
}
@@ -1,33 +0,0 @@
using Content.Shared.Damage.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared.EntityConditions.Conditions;
/// <summary>
/// Returns true if this entity can take damage and if its total damage is within a specified minimum and maximum.
/// </summary>
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
public sealed partial class TotalDamageEntityConditionSystem : EntityConditionSystem<DamageableComponent, TotalDamageCondition>
{
protected override void Condition(Entity<DamageableComponent> entity, ref EntityConditionEvent<TotalDamageCondition> args)
{
var total = entity.Comp.TotalDamage;
args.Result = total >= args.Condition.Min && total <= args.Condition.Max;
}
}
/// <inheritdoc cref="EntityCondition"/>
public sealed partial class TotalDamageCondition : EntityConditionBase<TotalDamageCondition>
{
[DataField]
public FixedPoint2 Max = FixedPoint2.MaxValue;
[DataField]
public FixedPoint2 Min = FixedPoint2.Zero;
public override string EntityConditionGuidebookText(IPrototypeManager prototype) =>
Loc.GetString("entity-condition-guidebook-total-damage",
("max", Max == FixedPoint2.MaxValue ? int.MaxValue : Max.Float()),
("min", Min.Float()));
}
@@ -1,5 +1,6 @@
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Damage.Systems;
using Content.Shared.Destructible;
using Content.Shared.Explosion;
using Content.Shared.FixedPoint;
@@ -13,6 +14,8 @@ namespace Content.Shared.GhostTypes;
public sealed class StoreDamageTakenOnMindSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
SubscribeLocalEvent<StoreDamageTakenOnMindComponent, DestructionEventArgs>(SaveBodyOnGib);
@@ -57,7 +60,7 @@ public sealed class StoreDamageTakenOnMindSystem : EntitySystem
EnsureComp<LastBodyDamageComponent>(mindContainer.Mind.Value, out var storedDamage);
var protoDict = new Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2>();
foreach (var stringDict in damageable.DamagePerGroup) // Translates the strings into ProtoId's before saving the Dictionary
foreach (var stringDict in _damageable.GetDamagePerGroup((ent, damageable))) // Translates the strings into ProtoId's before saving the Dictionary
{
if (!_proto.TryIndex(stringDict.Key, out DamageGroupPrototype? proto))
continue;
@@ -65,7 +68,7 @@ public sealed class StoreDamageTakenOnMindSystem : EntitySystem
}
storedDamage.DamagePerGroup = protoDict;
storedDamage.Damage = damageable.Damage;
storedDamage.Damage = _damageable.GetAllDamage((ent, damageable));
Dirty(mindContainer.Mind.Value, storedDamage);
}
@@ -1,4 +1,5 @@
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
@@ -10,6 +11,7 @@ namespace Content.Shared.HealthExaminable;
public sealed class HealthExaminableSystem : EntitySystem
{
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -47,9 +49,10 @@ public sealed class HealthExaminableSystem : EntitySystem
var msg = new FormattedMessage();
var first = true;
var damageSpecifier = _damageable.GetAllDamage((uid, damage));
foreach (var type in component.ExaminableTypes)
{
if (!damage.Damage.DamageDict.TryGetValue(type, out var dmg))
if (!damageSpecifier.DamageDict.TryGetValue(type, out var dmg))
continue;
if (dmg == FixedPoint2.Zero)
@@ -132,19 +132,22 @@ public sealed class CryoPodUserMessage : BoundUserInterfaceMessage
public FixedPoint2? BeakerCapacity;
public List<ReagentQuantity>? Beaker;
public List<ReagentQuantity>? Injecting;
public bool HasDamage;
public CryoPodUserMessage(
GasAnalyzerComponent.GasMixEntry gasMix,
HealthAnalyzerUiState health,
FixedPoint2? beakerCapacity,
List<ReagentQuantity>? beaker,
List<ReagentQuantity>? injecting)
List<ReagentQuantity>? injecting,
bool hasDamage)
{
GasMix = gasMix;
Health = health;
BeakerCapacity = beakerCapacity;
Beaker = beaker;
Injecting = injecting;
HasDamage = hasDamage;
}
}
@@ -126,7 +126,7 @@ public sealed class HealingSystem : EntitySystem
private bool HasDamage(Entity<HealingComponent> healing, Entity<DamageableComponent> target)
{
var damageableDict = target.Comp.Damage.DamageDict;
var damageableDict = _damageable.GetAllDamage(target.AsNullable()).DamageDict;
var healingDict = healing.Comp.Damage.DamageDict;
foreach (var type in healingDict)
{
@@ -240,7 +240,7 @@ public sealed class HealingSystem : EntitySystem
if (!_mobThresholdSystem.TryGetThresholdForState(ent, MobState.Critical, out var amount, ent.Comp2))
return 1;
var percentDamage = (float)(ent.Comp1.TotalDamage / amount);
var percentDamage = (float)(_damageable.GetTotalDamage(ent) / amount);
//basically make it scale from 1 to the multiplier.
var output = percentDamage * (mod - 1) + 1;
@@ -209,9 +209,8 @@ public abstract class SharedDefibrillatorSystem : EntitySystem
_damageable.TryChangeDamage(target, ent.Comp.ZapHeal, true, origin: user);
if (TryComp<MobThresholdsComponent>(target, out var targetThresholds) &&
TryComp<DamageableComponent>(target, out var targetDamageable) &&
_mobThreshold.TryGetThresholdForState(target, MobState.Dead, out var threshold, targetThresholds) &&
targetDamageable.TotalDamage < threshold)
_damageable.GetTotalDamage(target) < threshold)
{
_mobState.ChangeMobState(target, MobState.Critical, targetMobState, user);
failedRevive = false;
@@ -1,5 +1,6 @@
using Content.Shared.Actions;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
@@ -18,6 +19,7 @@ public sealed class StethoscopeSystem : EntitySystem
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
// The damage type to "listen" for with the stethoscope.
private const string DamageToListenFor = "Asphyxiation";
@@ -96,9 +98,8 @@ public sealed class StethoscopeSystem : EntitySystem
// TODO: Add check for respirator component when it gets moved to shared.
// If the mob is dead or cannot asphyxiation damage, the popup shows nothing.
if (!TryComp<MobStateComponent>(target, out var mobState) ||
!TryComp<DamageableComponent>(target, out var damageComp) ||
_mobState.IsDead(target, mobState) ||
!damageComp.Damage.DamageDict.TryGetValue(DamageToListenFor, out var asphyxDmg))
!_damageable.GetAllDamage(target).DamageDict.TryGetValue(DamageToListenFor, out var asphyxDmg))
{
_popup.PopupPredicted(Loc.GetString("stethoscope-nothing"), target, user);
stethoscope.Comp.LastMeasuredDamage = null;
@@ -3,6 +3,7 @@ using Content.Shared.Access.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Clothing;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.DeviceNetwork;
using Content.Shared.DoAfter;
using Content.Shared.Emp;
@@ -40,6 +41,7 @@ public abstract class SharedSuitSensorSystem : EntitySystem
[Dependency] private readonly SharedIdCardSystem _idCardSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
private EntityQuery<SuitSensorComponent> _sensorQuery;
public override void Initialize()
@@ -375,9 +377,7 @@ public abstract class SharedSuitSensorSystem : EntitySystem
isAlive = !_mobStateSystem.IsDead(sensor.User.Value, mobState);
// get mob total damage
var totalDamage = 0;
if (TryComp<DamageableComponent>(sensor.User.Value, out var damageable))
totalDamage = damageable.TotalDamage.Int();
var totalDamage = _damageable.GetTotalDamage(sensor.User.Value).Int();
// Get mob total damage crit threshold
int? totalDamageThreshold = null;
@@ -15,6 +15,7 @@ public sealed class MobThresholdSystem : EntitySystem
{
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -270,7 +271,7 @@ public sealed class MobThresholdSystem : EntitySystem
if (!TryGetThresholdForState(target2, MobState.Dead, out var ent2DeadThreshold, threshold2))
ent2DeadThreshold = 0;
damage = (oldDamage.Damage / ent1DeadThreshold.Value) * ent2DeadThreshold.Value;
damage = (_damageable.GetAllDamage((target1, oldDamage)) / ent1DeadThreshold.Value) * ent2DeadThreshold.Value;
return true;
}
@@ -339,7 +340,7 @@ public sealed class MobThresholdSystem : EntitySystem
{
foreach (var (threshold, mobState) in thresholdsComponent.Thresholds.Reverse())
{
if (damageableComponent.TotalDamage < threshold)
if (_damageable.GetTotalDamage((target, damageableComponent)) < threshold)
continue;
TriggerThreshold(target, mobState, mobStateComponent, thresholdsComponent, origin);
@@ -405,7 +406,7 @@ public sealed class MobThresholdSystem : EntitySystem
}
if (TryGetNextState(target, currentMobState, out var nextState, threshold) &&
TryGetPercentageForState(target, nextState.Value, damageable.TotalDamage, out var percentage))
TryGetPercentageForState(target, nextState.Value, _damageable.GetTotalDamage((target, damageable)), out var percentage))
{
percentage = FixedPoint2.Clamp(percentage.Value, 0, 1);
@@ -28,7 +28,11 @@ public sealed partial class RepairableSystem : EntitySystem
if (args.Cancelled)
return;
if (!TryComp(ent.Owner, out DamageableComponent? damageable) || damageable.TotalDamage == 0)
if (!TryComp(ent.Owner, out DamageableComponent? damageable))
return;
var totalDamage = _damageableSystem.GetTotalDamage((ent.Owner, damageable));
if (totalDamage == 0)
return;
if (ent.Comp.DamageValue != null)
@@ -38,7 +42,7 @@ public sealed partial class RepairableSystem : EntitySystem
else
RepairAllDamage((ent, damageable), args.User);
args.Repeat = ent.Comp.AutoDoAfter && damageable.TotalDamage > 0;
args.Repeat = ent.Comp.AutoDoAfter && totalDamage > 0;
args.Args.Event.Repeat = args.Repeat;
args.Handled = true;
@@ -95,7 +99,7 @@ public sealed partial class RepairableSystem : EntitySystem
return;
// Only try repair the target if it is damaged
if (!TryComp<DamageableComponent>(ent.Owner, out var damageable) || damageable.TotalDamage == 0)
if (_damageableSystem.GetTotalDamage(ent.Owner) == 0)
return;
float delay = ent.Comp.DoAfterDelay;
@@ -44,27 +44,4 @@ public sealed partial class MedibotTreatment
/// </summary>
[DataField(required: true)]
public FixedPoint2 Quantity;
/// <summary>
/// Do nothing when the patient is at or below this total damage.
/// When null this will inject meds into completely healthy patients.
/// </summary>
[DataField]
public FixedPoint2? MinDamage;
/// <summary>
/// Do nothing when the patient is at or above this total damage.
/// Useful for tricordrazine which does nothing above 50 damage.
/// </summary>
[DataField]
public FixedPoint2? MaxDamage;
/// <summary>
/// Returns whether the treatment will probably work for an amount of damage.
/// Doesn't account for specific damage types only total amount.
/// </summary>
public bool IsValid(FixedPoint2 damage)
{
return (MaxDamage == null || damage < MaxDamage) && (MinDamage == null || damage > MinDamage);
}
}
@@ -3,6 +3,7 @@ using Robust.Shared.Serialization;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.DoAfter;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
@@ -25,6 +26,7 @@ public sealed class MedibotSystem : EntitySystem
[Dependency] private SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
public override void Initialize()
{
@@ -107,14 +109,14 @@ public sealed class MedibotSystem : EntitySystem
return false;
}
var total = damageable.TotalDamage;
var total = _damageable.GetTotalDamage((target, damageable));
if (total == 0 && !HasComp<EmaggedComponent>(medibot))
{
_popup.PopupClient(Loc.GetString("medibot-target-healthy"), medibot, medibot);
return false;
}
if (!TryGetTreatment(medibot.Comp, mobState.CurrentState, out var treatment) || !treatment.IsValid(total) && !manual) return false;
if (!TryGetTreatment(medibot.Comp, mobState.CurrentState, out var treatment) || !manual) return false;
return true;
}
@@ -136,9 +136,6 @@ reagent-desc-opporozidone= A difficult to synthesize cryogenic drug used to rege
reagent-name-arcryox = arcryox
reagent-desc-arcryox = A sickeningly blue cryogenics chemical that is able to heal extreme wounds even on the dead. It has trouble stabilizing patients however.
reagent-name-necrosol = necrosol
reagent-desc-necrosol = A necrotic substance that seems to be able to heal frozen corpses. It can treat and rejuvenate plants when applied in small doses.
reagent-name-aloxadone = aloxadone
reagent-desc-aloxadone = A cryogenics chemical. Used to treat severe burns and frostbite via regeneration of the affected tissue. Works regardless of the patient being alive or dead.
@@ -116,7 +116,6 @@
- type: PassiveDamage # Around 8 damage a minute healed
allowedStates:
- Alive
damageCap: 65
damage:
types:
Heat: -0.14
@@ -224,7 +224,6 @@
# This allows Vox to take their mask off temporarily to eat something without needing a trip to medbay afterwards.
allowedStates:
- Alive
damageCap: 20
damage:
types:
Heat: -0.07
@@ -182,7 +182,6 @@
- type: PassiveDamage
allowedStates:
- Alive
damageCap: 20
damage:
types:
Heat: -0.07
@@ -2588,7 +2588,6 @@
- type: PassiveDamage # Slight passive regen. Assuming one damage type, comes out to about 4 damage a minute from base.yml.
allowedStates:
- Alive
damageCap: 89
damage:
types:
Poison: -0.07
@@ -67,7 +67,6 @@
- type: PassiveDamage
allowedStates:
- Alive
damageCap: 20
damage:
types:
Heat: -0.2
@@ -224,7 +224,6 @@
allowedStates:
- Alive
- Dead
damageCap: 50
damage:
types:
Blunt: 0.11
@@ -321,8 +321,6 @@
Alive:
reagent: Tricordrazine
quantity: 30
minDamage: 0
maxDamage: 50
Critical:
reagent: Inaprovaline
quantity: 15
@@ -619,21 +619,6 @@
- ReagentId: Mannitol
Quantity: 30
- type: entity
id: ChemistryBottleNecrosol
suffix: necrosol
parent: BaseChemistryBottleFilled
components:
- type: Label
currentLabel: reagent-name-necrosol
- type: SolutionContainerManager
solutions:
drink:
maxVol: 30
reagents:
- ReagentId: Necrosol
Quantity: 30
- type: entity
id: ChemistryBottleOculine
suffix: oculine
@@ -1476,7 +1461,6 @@
- id: ChemistryBottleLeporazine
- id: ChemistryBottleLipozine
- id: ChemistryBottleMannitol
- id: ChemistryBottleNecrosol
- id: ChemistryBottleOculine
- id: ChemistryBottleOmnizine
- id: ChemistryBottleOpporozidone
@@ -149,7 +149,7 @@
reagents:
- ReagentId: Ash
Quantity: 25
- ReagentId: Necrosol
- ReagentId: Arcryox
Quantity: 25
- type: GuideHelp
guides:
@@ -11,7 +11,6 @@
weight: 5
reagents:
- Ichor
- Necrosol
- quantity: 5
weight: 4
reagents:
+2 -51
View File
@@ -817,9 +817,6 @@
Bloodstream:
effects:
- !type:EvenHealthChange
conditions:
- !type:TotalDamageCondition
max: 50
damage:
Brute: -1
Burn: -1
@@ -1160,8 +1157,6 @@
factor: 3
- !type:HealthChange
conditions:
- !type:TotalDamageCondition
max: 50
damage:
types:
Blunt: -0.2
@@ -1312,53 +1307,9 @@
conditions:
- !type:TemperatureCondition
max: 213.0
- !type:TotalDamageCondition
min: 199.9
damage:
Brute: -2
Burn: -2
- !type:EvenHealthChange
conditions:
- !type:TemperatureCondition
max: 213.0
- !type:TotalDamageCondition
min: 500
damage:
Brute: -4
Burn: -4
- type: reagent
id: Necrosol
name: reagent-name-necrosol
group: Medicine
desc: reagent-desc-necrosol
physicalDesc: reagent-physical-desc-necrotic
flavor: medicine
color: "#86a5bd"
worksOnTheDead: true
plantMetabolism:
- !type:PlantAdjustToxins
amount: -5
- !type:PlantAdjustHealth
amount: 5
- !type:PlantCryoxadone {}
metabolisms:
Bloodstream:
effects:
- !type:EvenHealthChange
conditions:
- !type:TemperatureCondition
max: 213.0
damage:
Brute: -2
Burn: -2
- !type:HealthChange
conditions:
- !type:TemperatureCondition
max: 213.0
damage:
types:
Poison: -2
Brute: -3
Burn: -3
- type: reagent
id : Aloxadone
+4 -4
View File
@@ -190,15 +190,15 @@
amount: -1.5
- !type:EvenHealthChange
conditions:
- !type:TotalDamageCondition
min: 70 # only heals when you're more dead than alive
- !type:MobStateCondition
mobstate: Critical
damage: # Doesn't heal poison because if you OD'd I'm not giving you a safety net
Burn: -0.5
Brute: -0.5
- !type:HealthChange
conditions:
- !type:TotalDamageCondition
min: 95 # Just to bring you back from the brink of death
- !type:MobStateCondition
mobstate: Critical
damage:
types:
Asphyxiation: -2
@@ -583,20 +583,6 @@
products:
Arcryox: 3
- type: reaction
id: Necrosol
impact: Medium
minTemp: 370
reactants:
Blood:
amount: 3
Omnizine:
amount: 1
Cryoxadone:
amount: 2
products:
Necrosol: 2
- type: reaction
id: Aloxadone
impact: Medium
@@ -44,11 +44,11 @@ Once things have been set up, you're going to require a specific medication, Cry
The standard pressure for a gas pump is 100.325 kpa. Cryoxadone works at under 170K, but it is standard practice to set the freezer to 100K for faster freezing.
<GuideReagentEmbed Reagent="Aloxadone"/>
<GuideReagentEmbed Reagent="Arcryox"/>
<GuideReagentEmbed Reagent="Cryoxadone"/>
<GuideReagentEmbed Reagent="Doxarubixadone"/>
<GuideReagentEmbed Reagent="Dexalin"/>
<GuideReagentEmbed Reagent="Leporazine"/>
<GuideReagentEmbed Reagent="Necrosol"/>
<GuideReagentEmbed Reagent="Opporozidone"/>
</Document>
@@ -82,7 +82,6 @@ Some chemicals have special effects on plants.
<GuideReagentEmbed Reagent="Dylovene"/>
<GuideReagentEmbed Reagent="Cryoxadone"/>
<GuideReagentEmbed Reagent="Necrosol"/>
<GuideReagentEmbed Reagent="Phalanximine"/>
<GuideReagentGroupEmbed Group="Botanical"/>