Overhauled stamina slowdown behavior (#36336)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
@@ -56,4 +57,22 @@ public sealed partial class StaminaComponent : Component
|
||||
|
||||
[DataField]
|
||||
public ProtoId<AlertPrototype> StaminaAlert = "Stamina";
|
||||
|
||||
/// <summary>
|
||||
/// This flag indicates whether the value of <see cref="StaminaDamage"/> decreases after the entity exits stamina crit.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool AfterCritical;
|
||||
|
||||
/// <summary>
|
||||
/// This float determines how fast stamina will regenerate after exiting the stamina crit.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float AfterCritDecayMultiplier = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Thresholds that determine an entity's slowdown as a function of stamina damage.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<FixedPoint2, float> StunModifierThresholds = new() { {0, 1f }, { 60, 0.7f }, { 80, 0.5f } };
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Effects;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Rounding;
|
||||
@@ -21,7 +20,6 @@ using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Damage.Systems;
|
||||
@@ -114,6 +112,7 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
}
|
||||
|
||||
component.StaminaDamage = 0;
|
||||
AdjustSlowdown(uid);
|
||||
RemComp<ActiveStaminaComponent>(uid);
|
||||
SetStaminaAlert(uid, component);
|
||||
Dirty(uid, component);
|
||||
@@ -275,17 +274,16 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
component.NextUpdate = nextUpdate;
|
||||
}
|
||||
|
||||
var slowdownThreshold = component.CritThreshold / 2f;
|
||||
|
||||
// If we go above n% then apply slowdown
|
||||
if (oldDamage < slowdownThreshold &&
|
||||
component.StaminaDamage > slowdownThreshold)
|
||||
{
|
||||
_stunSystem.TrySlowdown(uid, TimeSpan.FromSeconds(3), true, 0.8f, 0.8f);
|
||||
}
|
||||
AdjustSlowdown(uid);
|
||||
|
||||
SetStaminaAlert(uid, component);
|
||||
|
||||
// Checking if the stamina damage has decreased to zero after exiting the stamcrit
|
||||
if (component.AfterCritical && oldDamage > component.StaminaDamage && component.StaminaDamage <= 0f)
|
||||
{
|
||||
component.AfterCritical = false; // Since the recovery from the crit has been completed, we are no longer 'after crit'
|
||||
}
|
||||
|
||||
if (!component.Critical)
|
||||
{
|
||||
if (component.StaminaDamage >= component.CritThreshold)
|
||||
@@ -330,9 +328,6 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
var stamQuery = GetEntityQuery<StaminaComponent>();
|
||||
var query = EntityQueryEnumerator<ActiveStaminaComponent>();
|
||||
var curTime = _timing.CurTime;
|
||||
@@ -353,15 +348,17 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
if (nextUpdate > curTime)
|
||||
continue;
|
||||
|
||||
// We were in crit so come out of it and continue.
|
||||
// Handle exiting critical condition and restoring stamina damage
|
||||
if (comp.Critical)
|
||||
{
|
||||
ExitStamCrit(uid, comp);
|
||||
continue;
|
||||
}
|
||||
|
||||
comp.NextUpdate += TimeSpan.FromSeconds(1f);
|
||||
TakeStaminaDamage(uid, -comp.Decay, comp);
|
||||
|
||||
TakeStaminaDamage(
|
||||
uid,
|
||||
comp.AfterCritical ? -comp.Decay * comp.AfterCritDecayMultiplier : -comp.Decay, // Recover faster after crit
|
||||
comp);
|
||||
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
}
|
||||
@@ -398,11 +395,38 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
}
|
||||
|
||||
component.Critical = false;
|
||||
component.StaminaDamage = 0f;
|
||||
component.AfterCritical = true; // Set to true to indicate that stamina will be restored after exiting stamcrit
|
||||
component.NextUpdate = _timing.CurTime;
|
||||
|
||||
SetStaminaAlert(uid, component);
|
||||
RemComp<ActiveStaminaComponent>(uid);
|
||||
Dirty(uid, component);
|
||||
_adminLogger.Add(LogType.Stamina, LogImpact.Low, $"{ToPrettyString(uid):user} recovered from stamina crit");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the movement speed of an entity based on its current <see cref="StaminaComponent.StaminaDamage"/> value.
|
||||
/// If the entity has a <see cref="SlowOnDamageComponent"/>, its custom damage-to-speed thresholds are used,
|
||||
/// otherwise, a default set of thresholds is applied.
|
||||
/// The method determines the closest applicable damage threshold below the crit limit and applies the corresponding
|
||||
/// speed modifier using the stun system. If no threshold is met then the entity's speed is restored to normal.
|
||||
/// </summary>
|
||||
/// <param name="ent">Entity to update</param>
|
||||
private void AdjustSlowdown(Entity<StaminaComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
var closest = FixedPoint2.Zero;
|
||||
|
||||
// Iterate through the dictionary in the similar way as in Damage.SlowOnDamageSystem.OnRefreshMovespeed
|
||||
foreach (var thres in ent.Comp.StunModifierThresholds)
|
||||
{
|
||||
var key = thres.Key.Float();
|
||||
|
||||
if (ent.Comp.StaminaDamage >= key && key > closest && closest < ent.Comp.CritThreshold)
|
||||
closest = thres.Key;
|
||||
}
|
||||
|
||||
_stunSystem.UpdateStunModifiers(ent, ent.Comp.StunModifierThresholds[closest]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Mobs;
|
||||
@@ -248,13 +249,63 @@ public abstract class SharedStunSystem : EntitySystem
|
||||
slowed.SprintSpeedModifier *= runSpeedMultiplier;
|
||||
|
||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the movement speed modifiers of an entity by applying or removing the <see cref="SlowedDownComponent"/>.
|
||||
/// If both walk and run modifiers are approximately 1 (i.e. normal speed) and <see cref="StaminaComponent.StaminaDamage"/> is 0,
|
||||
/// or if the both modifiers are 0, the slowdown component is removed to restore normal movement.
|
||||
/// Otherwise, the slowdown component is created or updated with the provided modifiers,
|
||||
/// and the movement speed is refreshed accordingly.
|
||||
/// </summary>
|
||||
/// <param name="ent">Entity whose movement speed should be updated.</param>
|
||||
/// <param name="walkSpeedModifier">New walk speed modifier. Default is 1f (normal speed).</param>
|
||||
/// <param name="runSpeedModifier">New run (sprint) speed modifier. Default is 1f (normal speed).</param>
|
||||
public void UpdateStunModifiers(Entity<StaminaComponent?> ent,
|
||||
float walkSpeedModifier = 1f,
|
||||
float runSpeedModifier = 1f)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
if (
|
||||
(MathHelper.CloseTo(walkSpeedModifier, 1f) && MathHelper.CloseTo(runSpeedModifier, 1f) && ent.Comp.StaminaDamage == 0f) ||
|
||||
(walkSpeedModifier == 0f && runSpeedModifier == 0f)
|
||||
)
|
||||
{
|
||||
RemComp<SlowedDownComponent>(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureComp<SlowedDownComponent>(ent, out var comp);
|
||||
|
||||
comp.WalkSpeedModifier = walkSpeedModifier;
|
||||
|
||||
comp.SprintSpeedModifier = runSpeedModifier;
|
||||
|
||||
_movementSpeedModifier.RefreshMovementSpeedModifiers(ent);
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A convenience overload of <see cref="UpdateStunModifiers(EntityUid, float, float, StaminaComponent?)"/> that sets both
|
||||
/// walk and run speed modifiers to the same value.
|
||||
/// </summary>
|
||||
/// <param name="ent">Entity whose movement speed should be updated.</param>
|
||||
/// <param name="speedModifier">New walk and run speed modifier. Default is 1f (normal speed).</param>
|
||||
/// <param name="component">
|
||||
/// Optional <see cref="StaminaComponent"/> of the entity.
|
||||
/// </param>
|
||||
public void UpdateStunModifiers(Entity<StaminaComponent?> ent, float speedModifier = 1f)
|
||||
{
|
||||
UpdateStunModifiers(ent, speedModifier, speedModifier);
|
||||
}
|
||||
|
||||
private void OnInteractHand(EntityUid uid, KnockedDownComponent knocked, InteractHandEvent args)
|
||||
{
|
||||
if (args.Handled || knocked.HelpTimer > 0f)
|
||||
|
||||
Reference in New Issue
Block a user