diff --git a/Content.Shared/Damage/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs
similarity index 100%
rename from Content.Shared/Damage/DamageableComponent.cs
rename to Content.Shared/Damage/Components/DamageableComponent.cs
diff --git a/Content.Shared/Damage/Components/SlowOnDamageComponent.cs b/Content.Shared/Damage/Components/SlowOnDamageComponent.cs
new file mode 100644
index 0000000000..5381549ef4
--- /dev/null
+++ b/Content.Shared/Damage/Components/SlowOnDamageComponent.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using Content.Shared.FixedPoint;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.Manager.Attributes;
+
+namespace Content.Shared.Damage.Components
+{
+ // TODO It'd be nice if this could be a destructible threshold, but on the other hand,
+ // that doesn't really work with events at all, and
+ [RegisterComponent, NetworkedComponent]
+ public class SlowOnDamageComponent : Component
+ {
+ public override string Name => "SlowOnDamage";
+
+ ///
+ /// Damage -> movespeed dictionary. This is -damage-, not -health-.
+ ///
+ [DataField("speedModifierThresholds", required: true)]
+ public readonly Dictionary SpeedModifierThresholds = default!;
+ }
+}
diff --git a/Content.Shared/Damage/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs
similarity index 99%
rename from Content.Shared/Damage/DamageableSystem.cs
rename to Content.Shared/Damage/Systems/DamageableSystem.cs
index f20773e853..5f47981d4a 100644
--- a/Content.Shared/Damage/DamageableSystem.cs
+++ b/Content.Shared/Damage/Systems/DamageableSystem.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
+using Content.Shared.Movement.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
diff --git a/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs b/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs
new file mode 100644
index 0000000000..b7d605f438
--- /dev/null
+++ b/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs
@@ -0,0 +1,54 @@
+using System;
+using Content.Shared.Damage.Components;
+using Content.Shared.FixedPoint;
+using Content.Shared.Movement.EntitySystems;
+using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
+
+namespace Content.Shared.Damage.Systems
+{
+ public class SlowOnDamageSystem : EntitySystem
+ {
+ [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnDamageChanged);
+ SubscribeLocalEvent(OnRefreshMovespeed);
+ }
+
+ private void OnRefreshMovespeed(EntityUid uid, SlowOnDamageComponent component, RefreshMovementSpeedModifiersEvent args)
+ {
+ if (!EntityManager.TryGetComponent(uid, out var damage))
+ return;
+
+ if (damage.TotalDamage == FixedPoint2.Zero)
+ return;
+
+ // Get closest threshold
+ FixedPoint2 closest = FixedPoint2.Zero;
+ var total = damage.TotalDamage;
+ foreach (var thres in component.SpeedModifierThresholds)
+ {
+ if (FixedPoint2.Dist(thres.Key, total) < FixedPoint2.Dist(closest, total))
+ closest = thres.Key;
+ }
+
+ if (closest != FixedPoint2.Zero)
+ {
+ var speed = component.SpeedModifierThresholds[closest];
+ args.ModifySpeed(speed, speed);
+ }
+ }
+
+ private void OnDamageChanged(EntityUid uid, SlowOnDamageComponent component, DamageChangedEvent args)
+ {
+ // We -could- only refresh if it crossed a threshold but that would kind of be a lot of duplicated
+ // code and this isn't a super hot path anyway since basically only humans have this
+
+ _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(uid);
+ }
+ }
+}
diff --git a/Content.Shared/FixedPoint/FixedPoint2.cs b/Content.Shared/FixedPoint/FixedPoint2.cs
index 0b1bb1afb6..bfca9c9478 100644
--- a/Content.Shared/FixedPoint/FixedPoint2.cs
+++ b/Content.Shared/FixedPoint/FixedPoint2.cs
@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Content.Shared.ActionBlocker;
using Robust.Shared.Serialization;
namespace Content.Shared.FixedPoint
@@ -210,6 +211,16 @@ namespace Content.Shared.FixedPoint
return a > b ? a : b;
}
+ public static FixedPoint2 Abs(FixedPoint2 a)
+ {
+ return FixedPoint2.New(Math.Abs(a._value));
+ }
+
+ public static FixedPoint2 Dist(FixedPoint2 a, FixedPoint2 b)
+ {
+ return FixedPoint2.Abs(a - b);
+ }
+
public static FixedPoint2 Clamp(FixedPoint2 reagent, FixedPoint2 min, FixedPoint2 max)
{
if (min > max)
diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml
index 0491907b2a..b8b1b136cd 100644
--- a/Resources/Prototypes/Entities/Mobs/Species/human.yml
+++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml
@@ -232,6 +232,12 @@
damage: 400
behaviors:
- !type:GibBehavior { }
+ - type: SlowOnDamage
+ speedModifierThresholds:
+ 40: 0.8
+ 60: 0.6
+ 80: 0.4
+ 90: 0.2
- type: HeatResistance
- type: Appearance
visuals: