Make StaminaModifier into a status effect, apply to Hyperzine (#41902)

* Initial commit

* Probably better this way.

* Review fixes

* cleanup

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
SlamBamActionman 2025-12-18 20:41:08 +01:00 committed by BarryNorfolk
parent 713159fa1a
commit d514f400a4
21 changed files with 96 additions and 58 deletions

View File

@ -39,9 +39,15 @@ public sealed partial class StaminaComponent : Component
public float StaminaDamage;
/// <summary>
/// How much stamina damage is required to enter stam crit.
/// The base stamina the entity requires to enter stam crit. Should rarely if ever be modified outside of yaml.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
[DataField, AutoNetworkedField]
public float BaseCritThreshold = 100f;
/// <summary>
/// Modified crit threshold for when an entity should enter stamcrit.
/// </summary>
[ViewVariables, AutoNetworkedField]
public float CritThreshold = 100f;
/// <summary>

View File

@ -7,7 +7,7 @@ namespace Content.Shared.Damage.Components;
/// Multiplies the entity's <see cref="StaminaComponent.StaminaDamage"/> by the <see cref="Modifier"/>.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStaminaSystem))]
public sealed partial class StaminaModifierComponent : Component
public sealed partial class StaminaModifierStatusEffectComponent : Component
{
/// <summary>
/// What to multiply max stamina by.

View File

@ -0,0 +1,18 @@
using Content.Shared.Damage.Components;
namespace Content.Shared.Damage.Events;
/// <summary>
/// Raised whenever the <see cref="StaminaComponent.CritThreshold"/> needs to be refreshed.
/// </summary>
[ByRefEvent]
public record struct RefreshStaminaCritThresholdEvent
{
public float ThresholdValue = 100f;
public float Modifier = 1f;
public RefreshStaminaCritThresholdEvent(float thresholdValue)
{
ThresholdValue = thresholdValue;
}
}

View File

@ -1,4 +1,6 @@
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Events;
using Content.Shared.StatusEffectNew;
namespace Content.Shared.Damage.Systems;
@ -6,47 +8,36 @@ public partial class SharedStaminaSystem
{
private void InitializeModifier()
{
SubscribeLocalEvent<StaminaModifierComponent, ComponentStartup>(OnModifierStartup);
SubscribeLocalEvent<StaminaModifierComponent, ComponentShutdown>(OnModifierShutdown);
SubscribeLocalEvent<StaminaModifierStatusEffectComponent, StatusEffectAppliedEvent>(OnEffectApplied);
SubscribeLocalEvent<StaminaModifierStatusEffectComponent, StatusEffectRemovedEvent>(OnEffectRemoved);
SubscribeLocalEvent<StaminaModifierStatusEffectComponent, StatusEffectRelayedEvent<RefreshStaminaCritThresholdEvent>>(OnRefreshCritThreshold);
}
private void OnModifierStartup(EntityUid uid, StaminaModifierComponent comp, ComponentStartup args)
private void OnEffectApplied(Entity<StaminaModifierStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
{
if (!TryComp<StaminaComponent>(uid, out var stamina))
return;
stamina.CritThreshold *= comp.Modifier;
RefreshStaminaCritThreshold(args.Target);
}
private void OnModifierShutdown(EntityUid uid, StaminaModifierComponent comp, ComponentShutdown args)
private void OnEffectRemoved(Entity<StaminaModifierStatusEffectComponent> ent, ref StatusEffectRemovedEvent args)
{
if (!TryComp<StaminaComponent>(uid, out var stamina))
return;
stamina.CritThreshold /= comp.Modifier;
RefreshStaminaCritThreshold(args.Target);
}
/// <summary>
/// Change the stamina modifier for an entity.
/// If it has <see cref="StaminaComponent"/> it will also be updated.
/// </summary>
public void SetModifier(EntityUid uid, float modifier, StaminaComponent? stamina = null, StaminaModifierComponent? comp = null)
private void OnRefreshCritThreshold(Entity<StaminaModifierStatusEffectComponent> ent, ref StatusEffectRelayedEvent<RefreshStaminaCritThresholdEvent> args)
{
if (!Resolve(uid, ref comp))
return;
var old = comp.Modifier;
if (old.Equals(modifier))
return;
comp.Modifier = modifier;
Dirty(uid, comp);
if (Resolve(uid, ref stamina, false))
{
// scale to the new threshold, act as if it was removed then added
stamina.CritThreshold *= modifier / old;
var evArgs = args.Args;
evArgs.Modifier = Math.Max(ent.Comp.Modifier, evArgs.Modifier); // We only pick the highest value, to avoid stacking different status effects.
args.Args = evArgs;
}
public void RefreshStaminaCritThreshold(Entity<StaminaComponent?> entity)
{
if (!Resolve(entity, ref entity.Comp))
return;
var ev = new RefreshStaminaCritThresholdEvent(entity.Comp.BaseCritThreshold);
RaiseLocalEvent(entity, ref ev);
entity.Comp.CritThreshold = ev.ThresholdValue * ev.Modifier;
}
}

View File

@ -101,6 +101,9 @@ public abstract partial class SharedStaminaSystem : EntitySystem
private void OnStartup(Entity<StaminaComponent> entity, ref ComponentStartup args)
{
// Set the base threshold here since ModifiedCritThreshold can't be modified via yaml.
entity.Comp.CritThreshold = entity.Comp.BaseCritThreshold;
UpdateStaminaVisuals(entity);
}

View File

@ -27,6 +27,7 @@ public sealed partial class StatusEffectsSystem
SubscribeLocalEvent<StatusEffectContainerComponent, StandUpAttemptEvent>(RefRelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, StunEndAttemptEvent>(RefRelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, RefreshStaminaCritThresholdEvent>(RefRelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, BeforeForceSayEvent>(RelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, BeforeAlertSeverityCheckEvent>(RelayStatusEffectEvent);

View File

@ -116,7 +116,7 @@
0: Alive
10: Dead
- type: Stamina
critThreshold: 10
baseCritThreshold: 10
animationThreshold: 1
- type: DamageStateVisuals
states:

View File

@ -46,7 +46,7 @@
baseWalkSpeed: 2.5
baseSprintSpeed: 3.5
- type: Stamina
critThreshold: 100
baseCritThreshold: 100
- type: DamageStateVisuals
states:
Alive:
@ -252,7 +252,7 @@
0: Alive
150: Dead
- type: Stamina
critThreshold: 150
baseCritThreshold: 150
- type: DamageStateVisuals
states:
Alive:

View File

@ -28,7 +28,7 @@
0: Alive
120: Dead
- type: Stamina
critThreshold: 120
baseCritThreshold: 120
- type: Destructible
thresholds:
- trigger:

View File

@ -41,7 +41,7 @@
0: Alive
75: Dead
- type: Stamina
critThreshold: 50
baseCritThreshold: 50
animationThreshold: 25
- type: Butcherable
spawned:
@ -243,7 +243,7 @@
0: Alive
75: Dead
- type: Stamina
critThreshold: 50
baseCritThreshold: 50
animationThreshold: 25
- type: Butcherable
spawned:

View File

@ -50,7 +50,6 @@
allowed:
- Corporeal
- Electrocution
- StaminaModifier
- type: Fixtures
fixtures:
fix1:

View File

@ -248,7 +248,7 @@
- !type:GibBehavior
recursive: false
- type: Stamina
critThreshold: 60
baseCritThreshold: 60
- type: MeleeWeapon
soundHit:
path: /Audio/Weapons/bladeslice.ogg

View File

@ -84,7 +84,7 @@
baseWalkSpeed : 3
baseSprintSpeed : 4
- type: Stamina
critThreshold: 120
baseCritThreshold: 120
- type: Destructible
thresholds:
- trigger:

View File

@ -97,7 +97,6 @@
- Electrocution
- TemporaryBlindness
- Pacified
- StaminaModifier
- Flashed
- RadiationProtection
- Adrenaline

View File

@ -36,7 +36,7 @@
0: Alive
100: Dead
- type: Stamina
critThreshold: 150
baseCritThreshold: 150
- type: MovementAlwaysTouching
- type: Bloodstream
bloodReferenceSolution:
@ -157,7 +157,7 @@
Dead:
Base: kangaroo-space-dead
- type: Stamina
critThreshold: 180
baseCritThreshold: 180
- type: Inventory
speciesId: kangaroo
templateId: spacekangaroo
@ -198,7 +198,7 @@
0: Alive
90: Dead
- type: Stamina
critThreshold: 150
baseCritThreshold: 150
- type: DamageStateVisuals
states:
Alive:
@ -303,7 +303,7 @@
0: Alive
100: Dead
- type: Stamina
critThreshold: 150
baseCritThreshold: 150
- type: DamageStateVisuals
states:
Alive:

View File

@ -52,7 +52,7 @@
- !type:GibBehavior
recursive: false
- type: Stamina
critThreshold: 15
baseCritThreshold: 15
animationThreshold: 5
- type: MovementAlwaysTouching
- type: DamageStateVisuals

View File

@ -65,7 +65,7 @@
speedModifierThresholds:
25: 0.5
- type: Stamina
critThreshold: 200
baseCritThreshold: 200
- type: Bloodstream
bloodReferenceSolution:
reagents:
@ -148,7 +148,7 @@
0: Alive
250: Dead # DeltaV - Was 100
- type: Stamina
critThreshold: 300
baseCritThreshold: 300
- type: SlowOnDamage
speedModifierThresholds:
50: 0.7
@ -522,7 +522,7 @@
- type: ComplexInteraction
- type: MobState
- type: Stamina
critThreshold: 200
baseCritThreshold: 200
- type: Bloodstream
bloodReferenceSolution:
reagents:

View File

@ -127,7 +127,6 @@
- Muted
- TemporaryBlindness
- Pacified
- StaminaModifier
- Flashed
- RadiationProtection
- Adrenaline

View File

@ -62,3 +62,26 @@
parent: [ PainNumbnessTraitStatusEffect, MobStatusEffectDebuff ]
id: StatusEffectPainNumbness
name: pain numbness
- type: entity
parent: MobStatusEffectBase
id: StaminaModifierStatusEffect
components:
- type: StatusEffect
whitelist:
components:
- Stamina
- type: StaminaModifierStatusEffect
- type: entity
parent: StaminaModifierStatusEffect
name: 2x max stamina
id: StatusEffectDesoxyStamina
- type: entity
parent: StaminaModifierStatusEffect
id: StatusEffectStimulantsStamina
name: 1.5x max stamina
components:
- type: StaminaModifierStatusEffect
modifier: 1.5

View File

@ -35,9 +35,8 @@
- !type:MovementSpeedModifier
walkSpeedModifier: 1.20
sprintSpeedModifier: 1.20
- !type:GenericStatusEffect
key: StaminaModifier # You are on meth. You keep going.
component: StaminaModifier
- !type:ModifyStatusEffect
effectProto: StatusEffectDesoxyStamina # You are on meth. You keep going.
time: 3
- !type:GenericStatusEffect
key: Adrenaline
@ -153,6 +152,9 @@
- !type:MovementSpeedModifier
walkSpeedModifier: 1.25
sprintSpeedModifier: 1.25
- !type:ModifyStatusEffect
effectProto: StatusEffectStimulantsStamina # You are on meth. You keep going.
time: 3
- !type:ModifyStatusEffect
effectProto: StatusEffectStunned
time: 3.5

View File

@ -51,9 +51,6 @@
- type: statusEffect
id: RatvarianLanguage #Praise him
- type: statusEffect
id: StaminaModifier
- type: statusEffect
id: Flashed