using System.Diagnostics.CodeAnalysis;
using Content.Shared.StatusEffectNew.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared.StatusEffectNew;
public sealed partial class StatusEffectsSystem
{
///
/// 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 (duration == TimeSpan.Zero)
{
statusEffect = null;
return false;
}
// We check to make sure time is greater than zero here because sometimes you want to use TryAddStatusEffect to remove duration instead...
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 (duration <= TimeSpan.Zero)
{
statusEffect = null;
return false;
}
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 (duration <= TimeSpan.Zero)
{
statusEffect = null;
return false;
}
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 (!_containerQuery.TryComp(target, out var container))
return false;
foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? [])
{
var meta = MetaData(effect);
if (meta.EntityPrototype is null
|| meta.EntityPrototype != effectProto)
continue;
if (!_effectQuery.HasComp(effect))
return false;
PredictedQueueDel(effect);
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?.ContainedEntities ?? [])
{
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?.ContainedEntities ?? [])
{
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?.ContainedEntities ?? [])
{
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.EndEffectTime = TimeSpan.Zero;
foreach (var effect in status)
{
if (effect.Comp2.EndEffectTime == null)
{
time = (effect.Owner, null);
return true;
}
if (effect.Comp2.EndEffectTime > time.EndEffectTime)
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?.ContainedEntities ?? [])
{
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?.ContainedEntities ?? [])
{
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?.ContainedEntities ?? [])
{
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?.ContainedEntities ?? [])
{
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?.ContainedEntities ?? [])
{
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;
}
}