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; } }