/// <summary>
        ///     Slows down the mob's walking/running speed temporarily
        /// </summary>
        public bool TrySlowdown(EntityUid uid, TimeSpan time, bool refresh,
                                float walkSpeedMultiplier     = 1f, float runSpeedMultiplier = 1f,
                                StatusEffectsComponent?status = null,
                                SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status))
            {
                return(false);
            }

            // "Optional" component.
            Resolve(uid, ref alerts, false);

            if (time <= TimeSpan.Zero)
            {
                return(false);
            }

            if (_statusEffectSystem.TryAddStatusEffect <SlowedDownComponent>(uid, "SlowedDown", time, refresh, status, alerts))
            {
                var slowed = EntityManager.GetComponent <SlowedDownComponent>(uid);
                // Doesn't make much sense to have the "TrySlowdown" method speed up entities now does it?
                walkSpeedMultiplier = Math.Clamp(walkSpeedMultiplier, 0f, 1f);
                runSpeedMultiplier  = Math.Clamp(runSpeedMultiplier, 0f, 1f);

                slowed.WalkSpeedModifier   *= walkSpeedMultiplier;
                slowed.SprintSpeedModifier *= runSpeedMultiplier;

                _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(uid);

                return(true);
            }

            return(false);
        }
        /// <summary>
        ///     Applies a jitter effect to the specified entity.
        ///     You can apply this to any entity whatsoever, so be careful what you use it on!
        /// </summary>
        /// <remarks>
        ///     If the entity is already jittering, the jitter values will be updated but only if they're greater
        ///     than the current ones and <see cref="forceValueChange"/> is false.
        /// </remarks>
        /// <param name="uid">Entity in question.</param>
        /// <param name="time">For how much time to apply the effect.</param>
        /// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
        /// <param name="amplitude">Jitteriness of the animation. See <see cref="MaxAmplitude"/> and <see cref="MinAmplitude"/>.</param>
        /// <param name="frequency">Frequency for jittering. See <see cref="MaxFrequency"/> and <see cref="MinFrequency"/>.</param>
        /// <param name="forceValueChange">Whether to change any existing jitter value even if they're greater than the ones we're setting.</param>
        /// <param name="status">The status effects component to modify.</param>
        /// <param name="alerts">The alerts component.</param>
        public void DoJitter(EntityUid uid, TimeSpan time, bool refresh, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false,
                             StatusEffectsComponent?status = null,
                             SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return;
            }

            amplitude = Math.Clamp(amplitude, MinAmplitude, MaxAmplitude);
            frequency = Math.Clamp(frequency, MinFrequency, MaxFrequency);

            if (StatusEffects.TryAddStatusEffect <JitteringComponent>(uid, "Jitter", time, refresh, status, alerts))
            {
                var jittering = EntityManager.GetComponent <JitteringComponent>(uid);

                if (forceValueChange || jittering.Amplitude < amplitude)
                {
                    jittering.Amplitude = amplitude;
                }

                if (forceValueChange || jittering.Frequency < frequency)
                {
                    jittering.Frequency = frequency;
                }
            }
        }
        /// <summary>
        ///     Tries to add a status effect to an entity, with a given component added as well.
        /// </summary>
        /// <param name="uid">The entity to add the effect to.</param>
        /// <param name="key">The status effect ID to add.</param>
        /// <param name="time">How long the effect should last for.</param>
        /// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
        /// <param name="status">The status effects component to change, if you already have it.</param>
        /// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
        /// <returns>False if the effect could not be added or the component already exists, true otherwise.</returns>
        /// <typeparam name="T">The component type to add and remove from the entity.</typeparam>
        public bool TryAddStatusEffect <T>(EntityUid uid, string key, TimeSpan time, bool refresh,
                                           StatusEffectsComponent?status = null,
                                           SharedAlertsComponent?alerts  = null)
            where T : Component, new()
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            if (TryAddStatusEffect(uid, key, time, refresh, status, alerts))
            {
                // If they already have the comp, we just won't bother updating anything.
                if (!EntityManager.HasComponent <T>(uid))
                {
                    var comp = EntityManager.AddComponent <T>(uid);
                    status.ActiveEffects[key].RelevantComponent = comp.Name;
                }
                return(true);
            }

            return(false);
        }
        public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component,
                                       StatusEffectsComponent?status = null,
                                       SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            if (TryAddStatusEffect(uid, key, time, refresh, status, alerts))
            {
                // If they already have the comp, we just won't bother updating anything.
                if (!EntityManager.HasComponent(uid, _componentFactory.GetRegistration(component).Type))
                {
                    // F**k this shit I hate it
                    var newComponent = (Component)_componentFactory.GetComponent(component);
                    newComponent.Owner = uid;

                    EntityManager.AddComponent(uid, newComponent);
                    status.ActiveEffects[key].RelevantComponent = component;
                }
                return(true);
            }

            return(false);
        }
        /// <summary>
        ///     Tries to add to the timer of an already existing status effect.
        /// </summary>
        /// <param name="uid">The entity to add time to.</param>
        /// <param name="key">The status effect to add time to.</param>
        /// <param name="time">The amount of time to add.</param>
        /// <param name="status">The status effect component, should you already have it.</param>
        public bool TryAddTime(EntityUid uid, string key, TimeSpan time,
                               StatusEffectsComponent?status = null,
                               SharedAlertsComponent?alert   = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }

            Resolve(uid, ref alert, false);

            if (!HasStatusEffect(uid, key, status))
            {
                return(false);
            }

            var timer = status.ActiveEffects[key].Cooldown;

            timer.Item2 += time;
            status.ActiveEffects[key].Cooldown = timer;

            if (_prototypeManager.TryIndex <StatusEffectPrototype>(key, out var proto) &&
                alert != null &&
                proto.Alert != null)
            {
                alert.ShowAlert(proto.Alert.Value, cooldown: GetAlertCooldown(uid, proto.Alert.Value, status));
            }

            return(true);
        }
        /// <summary>
        ///     Applies knockdown and stun to the entity temporarily.
        /// </summary>
        public bool TryParalyze(EntityUid uid, TimeSpan time,
                                StatusEffectsComponent?status = null,
                                SharedAlertsComponent?alerts  = null)
        {
            // Optional component.
            Resolve(uid, ref alerts, false);

            return(TryKnockdown(uid, time, status, alerts) && TryStun(uid, time, status, alerts));
        }
        /// <summary>
        ///     Tries to add a status effect to an entity with a certain timer.
        /// </summary>
        /// <param name="uid">The entity to add the effect to.</param>
        /// <param name="key">The status effect ID to add.</param>
        /// <param name="time">How long the effect should last for.</param>
        /// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
        /// <param name="status">The status effects component to change, if you already have it.</param>
        /// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
        /// <returns>False if the effect could not be added, or if the effect already existed.</returns>
        /// <remarks>
        ///     This obviously does not add any actual 'effects' on its own. Use the generic overload,
        ///     which takes in a component type, if you want to automatically add and remove a component.
        ///
        ///     If the effect already exists, it will simply replace the cooldown with the new one given.
        ///     If you want special 'effect merging' behavior, do it your own damn self!
        /// </remarks>
        public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh,
                                       StatusEffectsComponent?status = null,
                                       SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }
            if (!CanApplyEffect(uid, key, status))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            // we already checked if it has the index in CanApplyEffect so a straight index and not tryindex here
            // is fine
            var proto = _prototypeManager.Index <StatusEffectPrototype>(key);

            (TimeSpan, TimeSpan)cooldown = (_gameTiming.CurTime, _gameTiming.CurTime + time);

            if (HasStatusEffect(uid, key, status))
            {
                status.ActiveEffects[key].CooldownRefresh = refresh;
                if (refresh)
                {
                    //Making sure we don't reset a longer cooldown by applying a shorter one.
                    if ((status.ActiveEffects[key].Cooldown.Item2 - _gameTiming.CurTime) < time)
                    {
                        //Refresh cooldown time.
                        status.ActiveEffects[key].Cooldown = cooldown;
                    }
                }
                else
                {
                    //Accumulate cooldown time.
                    status.ActiveEffects[key].Cooldown.Item2 += time;
                }
            }
            else
            {
                status.ActiveEffects.Add(key, new StatusEffectState(cooldown, refresh, null));
            }

            if (proto.Alert != null && alerts != null)
            {
                alerts.ShowAlert(proto.Alert.Value, cooldown: GetAlertCooldown(uid, proto.Alert.Value, status));
            }

            status.Dirty();
            // event?
            return(true);
        }
        /// <summary>
        ///     Knocks down the entity, making it fall to the ground.
        /// </summary>
        public bool TryKnockdown(EntityUid uid, TimeSpan time,
                                 StatusEffectsComponent?status = null,
                                 SharedAlertsComponent?alerts  = null)
        {
            if (time <= TimeSpan.Zero)
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            return(_statusEffectSystem.TryAddStatusEffect <KnockedDownComponent>(uid, "KnockedDown", time, alerts: alerts));
        }
        /// <summary>
        ///     Attempts to remove a status effect from an entity.
        /// </summary>
        /// <param name="uid">The entity to remove an effect from.</param>
        /// <param name="key">The effect ID to remove.</param>
        /// <param name="status">The status effects component to change, if you already have it.</param>
        /// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
        /// <returns>False if the effect could not be removed, true otherwise.</returns>
        /// <remarks>
        ///     Obviously this doesn't automatically clear any effects a status effect might have.
        ///     That's up to the removed component to handle itself when it's removed.
        /// </remarks>
        public bool TryRemoveStatusEffect(EntityUid uid, string key,
                                          StatusEffectsComponent?status = null,
                                          SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }
            if (!status.ActiveEffects.ContainsKey(key))
            {
                return(false);
            }
            if (!_prototypeManager.TryIndex <StatusEffectPrototype>(key, out var proto))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            var state = status.ActiveEffects[key];

            // There are cases where a status effect component might be server-only, so TryGetRegistration...
            if (state.RelevantComponent != null && _componentFactory.TryGetRegistration(state.RelevantComponent, out var registration))
            {
                var type = registration.Type;

                // Make sure the component is actually there first.
                // Maybe a badmin badminned the component away,
                // or perhaps, on the client, the component deletion sync
                // was faster than prediction could predict. Either way, let's not assume the component exists.
                if (EntityManager.HasComponent(uid, type))
                {
                    EntityManager.RemoveComponent(uid, type);
                }
            }

            if (proto.Alert != null && alerts != null)
            {
                alerts.ClearAlert(proto.Alert.Value);
            }

            status.ActiveEffects.Remove(key);

            status.Dirty();
            // event?
            return(true);
        }
        // TODO STUN: Make events for different things. (Getting modifiers, attempt events, informative events...)

        /// <summary>
        ///     Stuns the entity, disallowing it from doing many interactions temporarily.
        /// </summary>
        public bool TryStun(EntityUid uid, TimeSpan time, bool refresh,
                            StatusEffectsComponent?status = null,
                            SharedAlertsComponent?alerts  = null)
        {
            if (time <= TimeSpan.Zero)
            {
                return(false);
            }

            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            return(_statusEffectSystem.TryAddStatusEffect <StunnedComponent>(uid, "Stun", time, refresh, alerts: alerts));
        }
        /// <summary>
        ///     Tries to add a status effect to an entity with a certain timer.
        /// </summary>
        /// <param name="uid">The entity to add the effect to.</param>
        /// <param name="key">The status effect ID to add.</param>
        /// <param name="time">How long the effect should last for.</param>
        /// <param name="status">The status effects component to change, if you already have it.</param>
        /// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
        /// <returns>False if the effect could not be added, or if the effect already existed.</returns>
        /// <remarks>
        ///     This obviously does not add any actual 'effects' on its own. Use the generic overload,
        ///     which takes in a component type, if you want to automatically add and remove a component.
        ///
        ///     If the effect already exists, it will simply replace the cooldown with the new one given.
        ///     If you want special 'effect merging' behavior, do it your own damn self!
        /// </remarks>
        public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time,
                                       StatusEffectsComponent?status = null,
                                       SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }
            if (!CanApplyEffect(uid, key, status))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            // we already checked if it has the index in CanApplyEffect so a straight index and not tryindex here
            // is fine
            var proto = _prototypeManager.Index <StatusEffectPrototype>(key);

            (TimeSpan, TimeSpan)cooldown = (_gameTiming.CurTime, _gameTiming.CurTime + time);

            // If they already have this status effect, just bulldoze its cooldown in favor of the new one
            // and keep the relevant component the same.
            if (HasStatusEffect(uid, key, status))
            {
                status.ActiveEffects[key] = new StatusEffectState(cooldown, status.ActiveEffects[key].RelevantComponent);
            }
            else
            {
                status.ActiveEffects.Add(key, new StatusEffectState(cooldown, null));
            }

            if (proto.Alert != null && alerts != null)
            {
                alerts.ShowAlert(proto.Alert.Value, cooldown: GetAlertCooldown(uid, proto.Alert.Value, status));
            }

            status.Dirty();
            // event?
            return(true);
        }
        /// <summary>
        ///     Tries to remove all status effects from a given entity.
        /// </summary>
        /// <param name="uid">The entity to remove effects from.</param>
        /// <param name="status">The status effects component to change, if you already have it.</param>
        /// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
        /// <returns>False if any status effects failed to be removed, true if they all did.</returns>
        public bool TryRemoveAllStatusEffects(EntityUid uid,
                                              StatusEffectsComponent?status = null,
                                              SharedAlertsComponent?alerts  = null)
        {
            if (!Resolve(uid, ref status, false))
            {
                return(false);
            }

            Resolve(uid, ref alerts, false);

            bool failed = false;

            foreach (var effect in status.ActiveEffects)
            {
                if (!TryRemoveStatusEffect(uid, effect.Key, status, alerts))
                {
                    failed = true;
                }
            }

            return(failed);
        }
Esempio n. 13
0
 // For code in shared... I imagine we ain't getting accent prediction anytime soon so let's not bother.
 public virtual void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent?status = null, SharedAlertsComponent?alerts = null)
 {
 }