diff --git a/Content.Shared/Damage/Components/StaminaResistanceComponent.cs b/Content.Shared/Damage/Components/StaminaResistanceComponent.cs
new file mode 100644
index 0000000000..1a0db54492
--- /dev/null
+++ b/Content.Shared/Damage/Components/StaminaResistanceComponent.cs
@@ -0,0 +1,38 @@
+using Content.Shared.Damage.Systems;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Damage.Components;
+
+///
+/// Component that provides entities with stamina resistance.
+/// By default this is applied when worn, but to solely protect the entity itself and
+/// not the wearer use worn: false.
+///
+///
+/// This is desirable over just using damage modifier sets, given that equipment like bomb-suits need to
+/// significantly reduce the damage, but shouldn't be silly overpowered in regular combat.
+///
+[NetworkedComponent, RegisterComponent, AutoGenerateComponentState]
+public sealed partial class StaminaResistanceComponent : Component
+{
+ ///
+ /// The stamina resistance coefficient, This fraction is multiplied into the total resistance.
+ ///
+ [DataField, AutoNetworkedField]
+ public float DamageCoefficient = 1;
+
+ ///
+ /// When true, resistances will be applied to the entity wearing this item.
+ /// When false, only this entity will get the resistance.
+ ///
+ [DataField]
+ public bool Worn = true;
+
+ ///
+ /// Examine string for stamina resistance.
+ /// Passed value from 0 to 100.
+ ///
+ [DataField]
+ public LocId Examine = "stamina-resistance-coefficient-value";
+}
diff --git a/Content.Shared/Damage/Events/BeforeStaminaDamageEvent.cs b/Content.Shared/Damage/Events/BeforeStaminaDamageEvent.cs
new file mode 100644
index 0000000000..6992ad83a5
--- /dev/null
+++ b/Content.Shared/Damage/Events/BeforeStaminaDamageEvent.cs
@@ -0,0 +1,12 @@
+using Content.Shared.Inventory;
+
+namespace Content.Shared.Damage.Events;
+
+///
+/// Raised before stamina damage is dealt to allow other systems to cancel or modify it.
+///
+[ByRefEvent]
+public record struct BeforeStaminaDamageEvent(float Value, bool Cancelled = false) : IInventoryRelayEvent
+{
+ SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET;
+}
diff --git a/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs b/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs
index 20e29ef434..4ccc56dcb8 100644
--- a/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs
+++ b/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs
@@ -1,4 +1,5 @@
using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Events;
using Content.Shared.Rejuvenate;
using Content.Shared.Slippery;
using Content.Shared.StatusEffect;
diff --git a/Content.Shared/Damage/Systems/StaminaSystem.Resistance.cs b/Content.Shared/Damage/Systems/StaminaSystem.Resistance.cs
new file mode 100644
index 0000000000..9ad09b8f2f
--- /dev/null
+++ b/Content.Shared/Damage/Systems/StaminaSystem.Resistance.cs
@@ -0,0 +1,38 @@
+using Content.Shared.Armor;
+using Content.Shared.Damage.Components;
+using Content.Shared.Damage.Events;
+using Content.Shared.Inventory;
+
+namespace Content.Shared.Damage.Systems;
+
+public sealed partial class StaminaSystem
+{
+ private void InitializeResistance()
+ {
+ SubscribeLocalEvent(OnGetResistance);
+ SubscribeLocalEvent>(RelayedResistance);
+ SubscribeLocalEvent(OnArmorExamine);
+ }
+
+ private void OnGetResistance(Entity ent, ref BeforeStaminaDamageEvent args)
+ {
+ args.Value *= ent.Comp.DamageCoefficient;
+ }
+
+ private void RelayedResistance(Entity ent, ref InventoryRelayedEvent args)
+ {
+ if (ent.Comp.Worn)
+ OnGetResistance(ent, ref args.Args);
+ }
+
+ private void OnArmorExamine(Entity ent, ref ArmorExamineEvent args)
+ {
+ var value = MathF.Round((1f - ent.Comp.DamageCoefficient) * 100, 1);
+
+ if (value == 0)
+ return;
+
+ args.Msg.PushNewline();
+ args.Msg.AddMarkupOrThrow(Loc.GetString(ent.Comp.Examine, ("value", value)));
+ }
+}
diff --git a/Content.Shared/Damage/Systems/StaminaSystem.cs b/Content.Shared/Damage/Systems/StaminaSystem.cs
index d897a363d4..bd84b711e3 100644
--- a/Content.Shared/Damage/Systems/StaminaSystem.cs
+++ b/Content.Shared/Damage/Systems/StaminaSystem.cs
@@ -50,6 +50,7 @@ public sealed partial class StaminaSystem : EntitySystem
base.Initialize();
InitializeModifier();
+ InitializeResistance();
SubscribeLocalEvent(OnStartup);
SubscribeLocalEvent(OnShutdown);
@@ -240,7 +241,7 @@ public sealed partial class StaminaSystem : EntitySystem
}
public void TakeStaminaDamage(EntityUid uid, float value, StaminaComponent? component = null,
- EntityUid? source = null, EntityUid? with = null, bool visual = true, SoundSpecifier? sound = null)
+ EntityUid? source = null, EntityUid? with = null, bool visual = true, SoundSpecifier? sound = null, bool ignoreResist = false)
{
if (!Resolve(uid, ref component, false))
return;
@@ -250,6 +251,12 @@ public sealed partial class StaminaSystem : EntitySystem
if (ev.Cancelled)
return;
+ // Allow stamina resistance to be applied.
+ if (!ignoreResist)
+ {
+ value = ev.Value;
+ }
+
value = UniversalStaminaDamageModifier * value;
// Have we already reached the point of max stamina damage?
@@ -399,9 +406,3 @@ public sealed partial class StaminaSystem : EntitySystem
_adminLogger.Add(LogType.Stamina, LogImpact.Low, $"{ToPrettyString(uid):user} recovered from stamina crit");
}
}
-
-///
-/// Raised before stamina damage is dealt to allow other systems to cancel it.
-///
-[ByRefEvent]
-public record struct BeforeStaminaDamageEvent(float Value, bool Cancelled = false);
diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs
index 8fac406eb5..efa88fb23a 100644
--- a/Content.Shared/Inventory/InventorySystem.Relay.cs
+++ b/Content.Shared/Inventory/InventorySystem.Relay.cs
@@ -5,6 +5,7 @@ using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Hypospray.Events;
using Content.Shared.Climbing.Events;
using Content.Shared.Damage;
+using Content.Shared.Damage.Events;
using Content.Shared.Electrocution;
using Content.Shared.Explosion;
using Content.Shared.Eye.Blinding.Systems;
@@ -45,6 +46,7 @@ public partial class InventorySystem
SubscribeLocalEvent(RelayInventoryEvent);
// by-ref events
+ SubscribeLocalEvent(RefRelayInventoryEvent);
SubscribeLocalEvent(RefRelayInventoryEvent);
SubscribeLocalEvent(RefRelayInventoryEvent);
SubscribeLocalEvent(RefRelayInventoryEvent);
diff --git a/Resources/Locale/en-US/damage/stamina.ftl b/Resources/Locale/en-US/damage/stamina.ftl
index da817824aa..cbda507865 100644
--- a/Resources/Locale/en-US/damage/stamina.ftl
+++ b/Resources/Locale/en-US/damage/stamina.ftl
@@ -1,2 +1,3 @@
melee-stamina = Not enough stamina
slow-on-damage-modifier-examine = Slowness from injuries is reduced by [color=yellow]{$mod}%[/color]
+stamina-resistance-coefficient-value = - [color=lightyellow]Stamina[/color] damage reduced by [color=lightblue]{$value}%[/color].
diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml
index 7835ace80f..acb44c8ad8 100644
--- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml
+++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml
@@ -130,6 +130,8 @@
Caustic: 0.5
- type: ExplosionResistance
damageCoefficient: 0.35
+ #- type: StaminaResistance
+ # damageCoefficient: 0.45
- type: ClothingSpeedModifier
walkModifier: 0.9
sprintModifier: 0.9
diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
index 6c2a671446..71eef999cf 100644
--- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
+++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
@@ -533,6 +533,8 @@
lowPressureMultiplier: 1000
- type: ExplosionResistance
damageCoefficient: 0.5
+ #- type: StaminaResistance
+ # damageCoefficient: 0.75
- type: Armor
modifiers:
coefficients:
@@ -593,6 +595,8 @@
damageCoefficient: 0.2
- type: FireProtection
reduction: 0.8
+ #- type: StaminaResistance
+ # damageCoefficient: 0.6
- type: Armor
modifiers:
coefficients:
@@ -627,6 +631,8 @@
lowPressureMultiplier: 1000
- type: ExplosionResistance
damageCoefficient: 0.5
+ #- type: StaminaResistance
+ # damageCoefficient: 0.6
- type: Armor
modifiers:
coefficients:
@@ -659,6 +665,8 @@
lowPressureMultiplier: 1000
- type: ExplosionResistance
damageCoefficient: 0.3
+ #- type: StaminaResistance # Should not have stamina resistance, this is purely so people know it was not forgotten.
+ # damageCoefficient: 0.99
- type: Armor
modifiers:
coefficients:
@@ -912,6 +920,8 @@
damageCoefficient: 0.2
- type: FireProtection
reduction: 0.8
+ - type: StaminaResistance
+ damageCoefficient: 0.15 # Needs 21 hits with a disabler to stun :godo:
- type: Armor
modifiers:
coefficients: