using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events;
using Content.Shared.StatusEffectNew;
using Robust.Shared.Prototypes;
namespace Content.Shared.Movement.Systems;
///
/// This handles the slowed status effect and other movement status effects.
/// holds a modifier for a status effect which is relayed to a mob's
/// All effects of this kinda are multiplicative.
/// Each 'source' of speed modification usually should have separate effect prototype.
///
///
/// Movement modifying status effects should by default be separate effect prototypes, and their effects
/// should stack with each other (multiply). In case multiplicative effect is undesirable - such effects
/// could occupy same prototype, but be aware that this will make controlling duration of effect
/// extra 'challenging', as it will be shared too.
///
public sealed class MovementModStatusSystem : EntitySystem
{
public static readonly EntProtoId VomitingSlowdown = "VomitingSlowdownStatusEffect";
public static readonly EntProtoId TaserSlowdown = "TaserSlowdownStatusEffect";
public static readonly EntProtoId FlashSlowdown = "FlashSlowdownStatusEffect";
public static readonly EntProtoId StatusEffectFriction = "StatusEffectFriction";
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
[Dependency] private readonly StatusEffectsSystem _status = default!;
public override void Initialize()
{
SubscribeLocalEvent(OnMovementModRemoved);
SubscribeLocalEvent>(OnRefreshRelay);
SubscribeLocalEvent(OnFrictionStatusEffectRemoved);
SubscribeLocalEvent>(OnRefreshFrictionStatus);
SubscribeLocalEvent>(OnRefreshTileFrictionStatus);
}
private void OnMovementModRemoved(Entity ent, ref StatusEffectRemovedEvent args)
{
TryUpdateMovementStatus(args.Target, (ent, ent), 1f);
}
private void OnFrictionStatusEffectRemoved(Entity entity, ref StatusEffectRemovedEvent args)
{
TrySetFrictionStatus(entity!, 1f, args.Target);
}
private void OnRefreshRelay(
Entity entity,
ref StatusEffectRelayedEvent args
)
{
args.Args.ModifySpeed(entity.Comp.WalkSpeedModifier, entity.Comp.WalkSpeedModifier);
}
private void OnRefreshFrictionStatus(Entity ent, ref StatusEffectRelayedEvent args)
{
var ev = args.Args;
ev.ModifyFriction(ent.Comp.FrictionModifier);
ev.ModifyAcceleration(ent.Comp.AccelerationModifier);
args.Args = ev;
}
private void OnRefreshTileFrictionStatus(Entity ent, ref StatusEffectRelayedEvent args)
{
var ev = args.Args;
ev.Modifier *= ent.Comp.FrictionModifier;
args.Args = ev;
}
///
/// Apply mob's walking/running speed modifier with provided duration, or increment duration of existing.
///
/// Target entity, for which speed should be modified.
/// Slowdown effect to be used.
/// Duration of speed modifying effect.
/// Multiplier by which walking/sprinting speed should be modified.
/// True if entity have slowdown effect applied now or previously and duration was modified.
public bool TryAddMovementSpeedModDuration(
EntityUid uid,
EntProtoId effectProtoId,
TimeSpan duration,
float speedModifier
)
{
return TryAddMovementSpeedModDuration(uid, effectProtoId, duration, speedModifier, speedModifier);
}
///
/// Apply mob's walking/running speed modifier with provided duration, or increment duration of existing.
///
/// Target entity, for which speed should be modified.
/// Slowdown effect to be used.
/// Duration of speed modifying effect.
/// Multiplier by which walking speed should be modified.
/// Multiplier by which sprinting speed should be modified.
/// True if entity have slowdown effect applied now or previously and duration was modified.
public bool TryAddMovementSpeedModDuration(
EntityUid uid,
EntProtoId effectProtoId,
TimeSpan duration,
float walkSpeedModifier,
float sprintSpeedModifier
)
{
return _status.TryAddStatusEffectDuration(uid, effectProtoId, out var status, duration)
&& TryUpdateMovementStatus(uid, status!.Value, walkSpeedModifier, sprintSpeedModifier);
}
///
/// Apply mob's walking/running speed modifier with provided duration,
/// or update duration of existing if it is lesser than provided duration.
///
/// Target entity, for which speed should be modified.
/// Slowdown effect to be used.
/// Duration of speed modifying effect.
/// Multiplier by which walking/sprinting speed should be modified.
/// True if entity have slowdown effect applied now or previously and duration was modified.
public bool TryUpdateMovementSpeedModDuration(
EntityUid uid,
EntProtoId effectProtoId,
TimeSpan duration,
float speedModifier
)
{
return TryUpdateMovementSpeedModDuration(uid, effectProtoId, duration, speedModifier, speedModifier);
}
///
/// Apply mob's walking/running speed modifier with provided duration,
/// or update duration of existing if it is lesser than provided duration.
///
/// Target entity, for which speed should be modified.
/// Slowdown effect to be used.
/// Duration of speed modifying effect.
/// Multiplier by which walking speed should be modified.
/// Multiplier by which sprinting speed should be modified.
/// True if entity have slowdown effect applied now or previously and duration was modified.
public bool TryUpdateMovementSpeedModDuration(
EntityUid uid,
EntProtoId effectProtoId,
TimeSpan? duration,
float walkSpeedModifier,
float sprintSpeedModifier
)
{
return _status.TryUpdateStatusEffectDuration(uid, effectProtoId, out var status, duration)
&& TryUpdateMovementStatus(uid, status!.Value, walkSpeedModifier, sprintSpeedModifier);
}
///
/// Updates entity's movement speed using to provided values.
/// Then refreshes the movement speed of the entity.
///
/// Entity whose component we're updating
/// Status effect entity whose modifiers we are updating
/// New walkSpeedModifer we're applying
/// New sprintSpeedModifier we're applying
public bool TryUpdateMovementStatus(
EntityUid uid,
Entity status,
float walkSpeedModifier,
float sprintSpeedModifier
)
{
if (!Resolve(status, ref status.Comp))
return false;
status.Comp.SprintSpeedModifier = sprintSpeedModifier;
status.Comp.WalkSpeedModifier = walkSpeedModifier;
_movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
return true;
}
///
/// Updates entity's movement speed using to provided value.
/// Then refreshes the movement speed of the entity.
///
/// Entity whose component we're updating
/// Status effect entity whose modifiers we are updating
///
/// Multiplier by which speed should be modified.
/// Will be applied to both walking and running speed.
///
public bool TryUpdateMovementStatus(
EntityUid uid,
Entity status,
float speedModifier
)
{
return TryUpdateMovementStatus(uid, status, speedModifier, speedModifier);
}
///
/// Apply friction modifier with provided duration,
/// or incrementing duration of existing.
///
/// Target entity, for which friction modifier should be applied.
/// Duration of speed modifying effect.
/// Multiplier by which walking speed should be modified.
/// Multiplier by which sprinting speed should be modified.
/// True if entity have slowdown effect applied now or previously and duration was modified.
public bool TryAddFrictionModDuration(
EntityUid uid,
TimeSpan duration,
float friction,
float acceleration
)
{
return _status.TryAddStatusEffectDuration(uid, StatusEffectFriction, out var status, duration)
&& TrySetFrictionStatus(status.Value, friction, acceleration, uid);
}
///
/// Apply friction modifier with provided duration,
/// or update duration of existing if it is lesser than provided duration.
///
/// Target entity, for which friction modifier should be applied.
/// Duration of speed modifying effect.
/// Multiplier by which walking speed should be modified.
/// Multiplier by which sprinting speed should be modified.
/// True if entity have slowdown effect applied now or previously and duration was modified.
public bool TryUpdateFrictionModDuration(
EntityUid uid,
TimeSpan duration,
float friction,
float acceleration
)
{
return _status.TryUpdateStatusEffectDuration(uid, StatusEffectFriction, out var status, duration)
&& TrySetFrictionStatus(status.Value, friction, acceleration, uid);
}
///
/// Sets the friction status modifiers for a status effect.
///
/// The status effect entity we're modifying.
/// The friction modifier we're applying.
/// The entity the status effect is attached to that we need to refresh.
private bool TrySetFrictionStatus(Entity status, float friction, EntityUid entity)
{
return TrySetFrictionStatus(status, friction, friction, entity);
}
///
/// Sets the friction status modifiers for a status effect.
///
/// The status effect entity we're modifying.
/// The friction modifier we're applying.
/// The acceleration modifier we're applying
/// The entity the status effect is attached to that we need to refresh.
private bool TrySetFrictionStatus(Entity status, float friction, float acceleration, EntityUid entity)
{
if (!Resolve(status, ref status.Comp, false))
return false;
status.Comp.FrictionModifier = friction;
status.Comp.AccelerationModifier = acceleration;
Dirty(status);
_movementSpeedModifier.RefreshFrictionModifiers(entity);
return true;
}
}