using System.Diagnostics.CodeAnalysis; using Content.Shared.StatusEffectNew.Components; using Robust.Shared.Prototypes; namespace Content.Shared.StatusEffectNew; public abstract partial class SharedStatusEffectsSystem { /// /// Increments duration of status effect by . /// Tries to add status effect if it is not yet present on entity. /// /// The target entity to which the effect should be added. /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. /// The EntityUid of the status effect we have just created or null if it doesn't exist. /// True if effect exists and its duration is set properly, false in case effect cannot be applied. public bool TryAddStatusEffectDuration( EntityUid target, EntProtoId effectProto, [NotNullWhen(true)] out EntityUid? statusEffect, TimeSpan duration ) { if (!TryGetStatusEffect(target, effectProto, out statusEffect)) return TryAddStatusEffect(target, effectProto, out statusEffect, duration); AddStatusEffectTime(statusEffect.Value, duration); return true; } /// public bool TryAddStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan duration) { return TryAddStatusEffectDuration(target, effectProto, out _, duration); } /// /// Sets duration of status effect by . /// Tries to add status effect if it is not yet present on entity. /// /// The target entity to which the effect should be added. /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. /// The EntityUid of the status effect we have just created or null if it doesn't exist. /// True if effect exists and its duration is set properly, false in case effect cannot be applied. public bool TrySetStatusEffectDuration( EntityUid target, EntProtoId effectProto, [NotNullWhen(true)] out EntityUid? statusEffect, TimeSpan? duration = null ) { if (!TryGetStatusEffect(target, effectProto, out statusEffect)) return TryAddStatusEffect(target, effectProto, out statusEffect, duration); SetStatusEffectTime(statusEffect.Value, duration); return true; } /// public bool TrySetStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null) { return TrySetStatusEffectDuration(target, effectProto, out _, duration); } /// /// Updates duration of effect to larger value between provided and current effect duration. /// Tries to add status effect if it is not yet present on entity. /// /// The target entity to which the effect should be added. /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. /// The EntityUid of the status effect we have just created or null if it doesn't exist. /// True if effect exists and its duration is set properly, false in case effect cannot be applied. public bool TryUpdateStatusEffectDuration( EntityUid target, EntProtoId effectProto, [NotNullWhen(true)] out EntityUid? statusEffect, TimeSpan? duration = null ) { if (!TryGetStatusEffect(target, effectProto, out statusEffect)) return TryAddStatusEffect(target, effectProto, out statusEffect, duration); UpdateStatusEffectTime(statusEffect.Value, duration); return true; } /// public bool TryUpdateStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null) { return TryUpdateStatusEffectDuration(target, effectProto, out _, duration); } /// /// Attempting to remove a status effect from an entity. /// Returns True if the status effect existed on the entity and was successfully removed, and False in otherwise. /// public bool TryRemoveStatusEffect(EntityUid target, EntProtoId effectProto) { if (_net.IsClient) //We cant remove the effect on the client (we need someone more robust at networking than me) return false; if (!_containerQuery.TryComp(target, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) { if (!_effectQuery.TryComp(effect, out var effectComp)) return false; var ev = new StatusEffectRemovedEvent(target); RaiseLocalEvent(effect, ref ev); QueueDel(effect); container.ActiveStatusEffects.Remove(effect); Dirty(target, container); return true; } } return false; } /// /// Checks whether the specified entity is under a specific status effect. /// public bool HasStatusEffect(EntityUid target, EntProtoId effectProto) { if (!_containerQuery.TryComp(target, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) return true; } return false; } /// /// Attempting to retrieve the EntityUid of a status effect from an entity. /// public bool TryGetStatusEffect(EntityUid target, EntProtoId effectProto, [NotNullWhen(true)] out EntityUid? effect) { effect = null; if (!_containerQuery.TryComp(target, out var container)) return false; foreach (var e in container.ActiveStatusEffects) { var meta = MetaData(e); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) { effect = e; return true; } } return false; } /// /// Attempting to retrieve the time of a status effect from an entity. /// /// The target entity on which the effect is applied. /// The prototype ID of the status effect to retrieve. /// The output tuple containing the effect entity and its remaining time. /// Optional. The status effect container component of the entity. public bool TryGetTime( EntityUid uid, EntProtoId effectProto, out (EntityUid EffectEnt, TimeSpan? EndEffectTime) time, StatusEffectContainerComponent? container = null ) { time = default; if (!Resolve(uid, ref container)) return false; foreach (var effect in container.ActiveStatusEffects) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) { if (!_effectQuery.TryComp(effect, out var effectComp)) return false; time = (effect, effectComp.EndEffectTime); return true; } } return false; } /// /// Attempts to get the maximum time left for a given Status Effect Component, returns false if no such /// component exists. /// /// The target entity on which the effect is applied. /// Returns the EntityUid of the status effect with the most time left, and the end effect time /// of that status effect. /// True if a status effect entity with the given component exists public bool TryGetMaxTime(EntityUid uid, out (EntityUid EffectEnt, TimeSpan? EndEffectTime) time) where T : IComponent { time = default; if (!TryEffectsWithComp(uid, out var status)) return false; time.Item2 = TimeSpan.Zero; foreach (var effect in status) { if (effect.Comp2.EndEffectTime == null) { time = (effect.Owner, null); return true; } if (effect.Comp2.EndEffectTime > time.Item2) time = (effect.Owner, effect.Comp2.EndEffectTime); } return true; } /// /// Attempts to edit the remaining time for a status effect on an entity. /// /// The target entity on which the effect is applied. /// The prototype ID of the status effect to modify. /// /// The time adjustment to apply to the status effect. Positive values extend the duration, /// while negative values reduce it. /// /// True if duration was edited successfully, false otherwise. public bool TryAddTime(EntityUid uid, EntProtoId effectProto, TimeSpan time) { if (!_containerQuery.TryComp(uid, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) { AddStatusEffectTime(effect, time); return true; } } return false; } /// /// Attempts to set the remaining time for a status effect on an entity. /// /// The target entity on which the effect is applied. /// The prototype ID of the status effect to modify. /// The new duration for the status effect. /// True if duration was set successfully, false otherwise. public bool TrySetTime(EntityUid uid, EntProtoId effectProto, TimeSpan time) { if (!_containerQuery.TryComp(uid, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) { SetStatusEffectTime(effect, time); return true; } } return false; } /// /// Checks if the specified component is present on any of the entity's status effects. /// public bool HasEffectComp(EntityUid? target) where T : IComponent { if (!_containerQuery.TryComp(target, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { if (HasComp(effect)) return true; } return false; } /// /// Returns all status effects that have the specified component. /// public bool TryEffectsWithComp(EntityUid? target, [NotNullWhen(true)] out HashSet>? effects) where T : IComponent { effects = null; if (!_containerQuery.TryComp(target, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { if (!_effectQuery.TryComp(effect, out var statusComp)) continue; if (TryComp(effect, out var comp)) { effects ??= []; effects.Add((effect, comp, statusComp)); } } return effects is not null; } /// /// Helper function for calculating how long it takes for all effects with a particular component to disappear. Useful for overlays. /// /// An entity from which status effects are checked. /// The farthest end time of effects with this component is returned. Can be null if one of the effects is infinite. /// True if effects with the specified component were found, or False if there are no such effects. public bool TryGetEffectsEndTimeWithComp(EntityUid? target, out TimeSpan? endTime) where T : IComponent { endTime = _timing.CurTime; if (!_containerQuery.TryComp(target, out var container)) return false; foreach (var effect in container.ActiveStatusEffects) { if (!HasComp(effect)) continue; if (!_effectQuery.TryComp(effect, out var statusComp)) continue; if (statusComp.EndEffectTime is null) { endTime = null; return true; //This effect never ends, so we return null at endTime, but return true that there is time. } if (statusComp.EndEffectTime > endTime) endTime = statusComp.EndEffectTime; } return endTime is not null; } }