2025-06-25 14:41:35 +03:00
using System.Diagnostics.CodeAnalysis ;
using Content.Shared.StatusEffectNew.Components ;
using Robust.Shared.Prototypes ;
namespace Content.Shared.StatusEffectNew ;
public abstract partial class SharedStatusEffectsSystem
{
/// <summary>
2025-07-02 10:46:30 -07:00
/// Increments duration of status effect by <see cref="duration"/>.
/// Tries to add status effect if it is not yet present on entity.
2025-06-25 14:41:35 +03:00
/// </summary>
/// <param name="target">The target entity to which the effect should be added.</param>
/// <param name="effectProto">ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it.</param>
/// <param name="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
2025-06-27 05:00:20 -07:00
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if it doesn't exist.</param>
2025-07-02 10:46:30 -07:00
/// <returns>True if effect exists and its duration is set properly, false in case effect cannot be applied.</returns>
public bool TryAddStatusEffectDuration (
2025-06-25 14:41:35 +03:00
EntityUid target ,
EntProtoId effectProto ,
2025-07-02 10:46:30 -07:00
[NotNullWhen(true)] out EntityUid ? statusEffect ,
TimeSpan duration
2025-06-25 14:41:35 +03:00
)
{
2025-07-02 10:46:30 -07:00
if ( ! TryGetStatusEffect ( target , effectProto , out statusEffect ) )
return TryAddStatusEffect ( target , effectProto , out statusEffect , duration ) ;
2025-06-25 14:41:35 +03:00
2025-07-02 10:46:30 -07:00
AddStatusEffectTime ( statusEffect . Value , duration ) ;
2025-06-25 14:41:35 +03:00
2025-07-02 10:46:30 -07:00
return true ;
}
2025-06-25 14:41:35 +03:00
2025-07-02 10:46:30 -07:00
///<inheritdoc cref="TryAddStatusEffectDuration(Robust.Shared.GameObjects.EntityUid,Robust.Shared.Prototypes.EntProtoId,out Robust.Shared.GameObjects.EntityUid?,System.TimeSpan)"/>
public bool TryAddStatusEffectDuration ( EntityUid target , EntProtoId effectProto , TimeSpan duration )
{
return TryAddStatusEffectDuration ( target , effectProto , out _ , duration ) ;
}
2025-06-25 14:41:35 +03:00
2025-07-02 10:46:30 -07:00
/// <summary>
/// Sets duration of status effect by <see cref="duration"/>.
/// Tries to add status effect if it is not yet present on entity.
/// </summary>
/// <param name="target">The target entity to which the effect should be added.</param>
/// <param name="effectProto">ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it.</param>
/// <param name="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if it doesn't exist.</param>
/// <returns>True if effect exists and its duration is set properly, false in case effect cannot be applied.</returns>
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 ) ;
2025-06-25 14:41:35 +03:00
2025-07-02 10:46:30 -07:00
SetStatusEffectTime ( statusEffect . Value , duration ) ;
2025-06-25 14:41:35 +03:00
return true ;
}
2025-07-02 10:46:30 -07:00
/// <inheritdoc cref="TrySetStatusEffectDuration(Robust.Shared.GameObjects.EntityUid,Robust.Shared.Prototypes.EntProtoId,out Robust.Shared.GameObjects.EntityUid?,System.TimeSpan?)"/>
public bool TrySetStatusEffectDuration ( EntityUid target , EntProtoId effectProto , TimeSpan ? duration = null )
{
return TrySetStatusEffectDuration ( target , effectProto , out _ , duration ) ;
}
2025-06-27 05:00:20 -07:00
/// <summary>
2025-07-02 10:46:30 -07:00
/// Updates duration of effect to larger value between provided <see cref="duration"/> and current effect duration.
/// Tries to add status effect if it is not yet present on entity.
2025-06-27 05:00:20 -07:00
/// </summary>
2025-07-02 10:46:30 -07:00
/// <param name="target">The target entity to which the effect should be added.</param>
/// <param name="effectProto">ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it.</param>
/// <param name="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if it doesn't exist.</param>
/// <returns>True if effect exists and its duration is set properly, false in case effect cannot be applied.</returns>
public bool TryUpdateStatusEffectDuration (
2025-06-27 05:00:20 -07:00
EntityUid target ,
EntProtoId effectProto ,
2025-07-02 10:46:30 -07:00
[NotNullWhen(true)] out EntityUid ? statusEffect ,
TimeSpan ? duration = null
2025-06-27 05:00:20 -07:00
)
{
2025-07-02 10:46:30 -07:00
if ( ! TryGetStatusEffect ( target , effectProto , out statusEffect ) )
return TryAddStatusEffect ( target , effectProto , out statusEffect , duration ) ;
UpdateStatusEffectTime ( statusEffect . Value , duration ) ;
return true ;
}
/// <inheritdoc cref="TryUpdateStatusEffectDuration(Robust.Shared.GameObjects.EntityUid,Robust.Shared.Prototypes.EntProtoId,out Robust.Shared.GameObjects.EntityUid?,System.TimeSpan?)"/>
public bool TryUpdateStatusEffectDuration ( EntityUid target , EntProtoId effectProto , TimeSpan ? duration = null )
{
return TryUpdateStatusEffectDuration ( target , effectProto , out _ , duration ) ;
2025-06-27 05:00:20 -07:00
}
2025-06-25 14:41:35 +03:00
/// <summary>
/// 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.
/// </summary>
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 ;
}
/// <summary>
/// Checks whether the specified entity is under a specific status effect.
/// </summary>
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 ;
}
/// <summary>
/// Attempting to retrieve the EntityUid of a status effect from an entity.
/// </summary>
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 ;
}
/// <summary>
/// Attempting to retrieve the time of a status effect from an entity.
/// </summary>
/// <param name="uid">The target entity on which the effect is applied.</param>
/// <param name="effectProto">The prototype ID of the status effect to retrieve.</param>
/// <param name="time">The output tuple containing the effect entity and its remaining time.</param>
/// <param name="container">Optional. The status effect container component of the entity.</param>
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 ;
}
2025-07-02 10:46:30 -07:00
/// <summary>
/// Attempts to get the maximum time left for a given Status Effect Component, returns false if no such
/// component exists.
/// </summary>
/// <param name="uid">The target entity on which the effect is applied.</param>
/// <param name="time">Returns the EntityUid of the status effect with the most time left, and the end effect time
/// of that status effect.</param>
/// <returns> True if a status effect entity with the given component exists</returns>
public bool TryGetMaxTime < T > ( EntityUid uid , out ( EntityUid EffectEnt , TimeSpan ? EndEffectTime ) time ) where T : IComponent
{
time = default ;
if ( ! TryEffectsWithComp < T > ( 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 ;
}
2025-06-25 14:41:35 +03:00
/// <summary>
/// Attempts to edit the remaining time for a status effect on an entity.
/// </summary>
/// <param name="uid">The target entity on which the effect is applied.</param>
/// <param name="effectProto">The prototype ID of the status effect to modify.</param>
/// <param name="time">
/// The time adjustment to apply to the status effect. Positive values extend the duration,
/// while negative values reduce it.
/// </param>
/// <returns> True if duration was edited successfully, false otherwise.</returns>
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 ;
}
/// <summary>
/// Attempts to set the remaining time for a status effect on an entity.
/// </summary>
/// <param name="uid">The target entity on which the effect is applied.</param>
/// <param name="effectProto">The prototype ID of the status effect to modify.</param>
/// <param name="time">The new duration for the status effect.</param>
/// <returns> True if duration was set successfully, false otherwise.</returns>
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 ;
}
/// <summary>
/// Checks if the specified component is present on any of the entity's status effects.
/// </summary>
public bool HasEffectComp < T > ( EntityUid ? target ) where T : IComponent
{
if ( ! _containerQuery . TryComp ( target , out var container ) )
return false ;
foreach ( var effect in container . ActiveStatusEffects )
{
if ( HasComp < T > ( effect ) )
return true ;
}
return false ;
}
/// <summary>
/// Returns all status effects that have the specified component.
/// </summary>
public bool TryEffectsWithComp < T > ( EntityUid ? target , [ NotNullWhen ( true ) ] out HashSet < Entity < T , StatusEffectComponent > > ? effects ) where T : IComponent
{
effects = null ;
if ( ! _containerQuery . TryComp ( target , out var container ) )
return false ;
foreach ( var effect in container . ActiveStatusEffects )
{
2025-06-27 21:19:04 +03:00
if ( ! _effectQuery . TryComp ( effect , out var statusComp ) )
2025-06-25 14:41:35 +03:00
continue ;
if ( TryComp < T > ( effect , out var comp ) )
{
effects ? ? = [ ] ;
effects . Add ( ( effect , comp , statusComp ) ) ;
}
}
2025-06-27 21:19:04 +03:00
return effects is not null ;
}
/// <summary>
/// Helper function for calculating how long it takes for all effects with a particular component to disappear. Useful for overlays.
/// </summary>
/// <param name="target">An entity from which status effects are checked.</param>
/// <param name="endTime">The farthest end time of effects with this component is returned. Can be null if one of the effects is infinite.</param>
/// <returns>True if effects with the specified component were found, or False if there are no such effects.</returns>
public bool TryGetEffectsEndTimeWithComp < T > ( 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 < T > ( 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 ;
2025-06-25 14:41:35 +03:00
}
}