using System.Numerics; using Content.Shared.Alert; using Content.Shared.FixedPoint; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Damage.Components; /// /// Add to an entity to paralyze it whenever it reaches critical amounts of Stamina DamageType. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause] public sealed partial class StaminaComponent : Component { /// /// Have we reached peak stamina damage and been paralyzed? /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public bool Critical; /// /// How much stamina reduces per second. /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public float Decay = 3f; /// /// How much time after receiving damage until stamina starts decreasing. /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public float Cooldown = 3f; /// /// How much stamina damage this entity has taken. /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public float StaminaDamage; /// /// How much stamina damage is required to enter stam crit. /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public float CritThreshold = 100f; /// /// How long will this mob be stunned for? /// [ViewVariables(VVAccess.ReadWrite), DataField] public TimeSpan StunTime = TimeSpan.FromSeconds(6); /// /// To avoid continuously updating our data we track the last time we updated so we can extrapolate our current stamina. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] [AutoPausedField] public TimeSpan NextUpdate = TimeSpan.Zero; [DataField] public ProtoId StaminaAlert = "Stamina"; /// /// This flag indicates whether the value of decreases after the entity exits stamina crit. /// [DataField, AutoNetworkedField] public bool AfterCritical; /// /// This float determines how fast stamina will regenerate after exiting the stamina crit. /// [DataField, AutoNetworkedField] public float AfterCritDecayMultiplier = 5f; /// /// This is how much stamina damage a mob takes when it forces itself to stand up before modifiers /// [DataField, AutoNetworkedField] public float ForceStandStamina = 10f; /// /// What sound should play when we successfully stand up /// [DataField, AutoNetworkedField] public SoundSpecifier ForceStandSuccessSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg"); /// /// Thresholds that determine an entity's slowdown as a function of stamina damage. /// [DataField] public Dictionary StunModifierThresholds = new() { {0, 1f }, { 60, 0.7f }, { 80, 0.5f } }; #region Animation Data /// /// Threshold at which low stamina animations begin playing. This should be set to a value that means something. /// At 50, it is aligned so when you hit 60 stun the entity will be breathing once per second (well above hyperventilation). /// [DataField] public float AnimationThreshold = 50; /// /// Minimum y vector displacement for breathing at AnimationThreshold /// [DataField] public float BreathingAmplitudeMin = 0.04f; /// /// Maximum y vector amount we add to the BreathingAmplitudeMin /// [DataField] public float BreathingAmplitudeMod = 0.04f; /// /// Minimum vector displacement for jittering at AnimationThreshold /// [DataField] public float JitterAmplitudeMin; /// /// Maximum vector amount we add to the JitterAmplitudeMin /// [DataField] public float JitterAmplitudeMod = 0.04f; /// /// Min multipliers for JitterAmplitude in the X and Y directions, animation randomly chooses between these min and max multipliers /// [DataField] public Vector2 JitterMin = Vector2.Create(0.5f, 0.125f); /// /// Max multipliers for JitterAmplitude in the X and Y directions, animation randomly chooses between these min and max multipliers /// [DataField] public Vector2 JitterMax = Vector2.Create(1f, 0.25f); /// /// Minimum total animations per second /// [DataField] public float FrequencyMin = 0.25f; /// /// Maximum amount we add to the Frequency min just before crit /// [DataField] public float FrequencyMod = 1.75f; /// /// Jitter keyframes per animation /// [DataField] public int Jitters = 4; /// /// Vector of the last Jitter so we can make sure we don't jitter in the same quadrant twice in a row. /// [DataField] public Vector2 LastJitter; /// /// The offset that an entity had before jittering started, /// so that we can reset it properly. /// [DataField] public Vector2 StartOffset = Vector2.Zero; #endregion }