Files
tbd-station-14/Content.Client/Damage/Systems/StaminaSystem.cs
Princess Cheeseballs 6ffec751ee Flesh Stun Fix (#39324)
2025-08-02 00:44:55 +03:00

130 lines
4.9 KiB
C#

using Content.Client.Stunnable;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.Damage.Systems;
public sealed partial class StaminaSystem : SharedStaminaSystem
{
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly StunSystem _stun = default!; // Clientside Stun System
private const string StaminaAnimationKey = "stamina";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StaminaComponent, AnimationCompletedEvent>(OnAnimationCompleted);
SubscribeLocalEvent<ActiveStaminaComponent, ComponentShutdown>(OnActiveStaminaShutdown);
SubscribeLocalEvent<StaminaComponent, MobStateChangedEvent>(OnMobStateChanged);
}
protected override void OnStamHandleState(Entity<StaminaComponent> entity, ref AfterAutoHandleStateEvent args)
{
base.OnStamHandleState(entity, ref args);
TryStartAnimation(entity);
}
private void OnActiveStaminaShutdown(Entity<ActiveStaminaComponent> entity, ref ComponentShutdown args)
{
// If we don't have active stamina, we shouldn't have stamina damage. If the update loop can trust it we can trust it.
if (!TryComp<StaminaComponent>(entity, out var stamina))
return;
StopAnimation((entity, stamina));
}
protected override void OnShutdown(Entity<StaminaComponent> entity, ref ComponentShutdown args)
{
base.OnShutdown(entity, ref args);
StopAnimation(entity);
}
private void OnMobStateChanged(Entity<StaminaComponent> ent, ref MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Dead)
StopAnimation(ent);
}
private void TryStartAnimation(Entity<StaminaComponent> entity)
{
if (!TryComp<SpriteComponent>(entity, out var sprite))
return;
// If the animation is running, the system should update it accordingly
// If we're below the threshold to animate, don't try to animate
// If we're in stamcrit don't override it
if (entity.Comp.AnimationThreshold > entity.Comp.StaminaDamage || _animation.HasRunningAnimation(entity, StaminaAnimationKey))
return;
// Don't animate if we're dead
if (_mobState.IsDead(entity))
return;
entity.Comp.StartOffset = sprite.Offset;
PlayAnimation((entity, entity.Comp, sprite));
}
private void StopAnimation(Entity<StaminaComponent, SpriteComponent?> entity)
{
if(!Resolve(entity, ref entity.Comp2))
return;
_animation.Stop(entity.Owner, StaminaAnimationKey);
entity.Comp1.StartOffset = entity.Comp2.Offset;
}
private void OnAnimationCompleted(Entity<StaminaComponent> entity, ref AnimationCompletedEvent args)
{
if (args.Key != StaminaAnimationKey || !args.Finished || !TryComp<SpriteComponent>(entity, out var sprite))
return;
// stop looping if we're below the threshold
if (entity.Comp.AnimationThreshold > entity.Comp.StaminaDamage)
{
_animation.Stop(entity.Owner, StaminaAnimationKey);
_sprite.SetOffset((entity, sprite), entity.Comp.StartOffset);
return;
}
if (!HasComp<AnimationPlayerComponent>(entity))
return;
PlayAnimation((entity, entity.Comp, sprite));
}
private void PlayAnimation(Entity<StaminaComponent, SpriteComponent> entity)
{
DebugTools.Assert(entity.Comp1.CritThreshold > entity.Comp1.AnimationThreshold, $"Animation threshold on {ToPrettyString(entity)} was not less than the crit threshold. This will cause errors, animation has been cancelled.");
var step = Math.Clamp((entity.Comp1.StaminaDamage - entity.Comp1.AnimationThreshold) /
(entity.Comp1.CritThreshold - entity.Comp1.AnimationThreshold),
0f,
1f); // The things I do for project 0 warnings
var frequency = entity.Comp1.FrequencyMin + step * entity.Comp1.FrequencyMod;
var jitter = entity.Comp1.JitterAmplitudeMin + step * entity.Comp1.JitterAmplitudeMod;
var breathing = entity.Comp1.BreathingAmplitudeMin + step * entity.Comp1.BreathingAmplitudeMod;
_animation.Play(entity.Owner,
_stun.GetFatigueAnimation(entity.Comp2,
frequency,
entity.Comp1.Jitters,
jitter * entity.Comp1.JitterMin,
jitter * entity.Comp1.JitterMax,
breathing,
entity.Comp1.StartOffset,
ref entity.Comp1.LastJitter),
StaminaAnimationKey);
}
}