+using Content.Shared.Bed.Sleep;
using Content.Shared.Drowsiness;
-using Content.Shared.StatusEffect;
+using Content.Shared.StatusEffectNew;
+using Content.Shared.StatusEffectNew.Components;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[Dependency] private readonly IGameTiming _timing = default!;
+ private readonly SharedStatusEffectsSystem _statusEffects = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
private readonly ShaderInstance _drowsinessShader;
+ private EntityQuery<StatusEffectComponent> _statusQuery;
+
public float CurrentPower = 0.0f;
private const float PowerDivisor = 250.0f;
public DrowsinessOverlay()
{
IoCManager.InjectDependencies(this);
+ _statusEffects = _sysMan.GetEntitySystem<SharedStatusEffectsSystem>();
+
+ _statusQuery = _entityManager.GetEntityQuery<StatusEffectComponent>();
_drowsinessShader = _prototypeManager.Index<ShaderPrototype>("Drowsiness").InstanceUnique();
}
if (playerEntity == null)
return;
- if (!_entityManager.HasComponent<DrowsinessComponent>(playerEntity)
- || !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
+ if (!_statusEffects.TryEffectsWithComp<DrowsinessStatusEffectComponent>(playerEntity, out var drowsinessEffects))
return;
- var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
- if (!statusSys.TryGetTime(playerEntity.Value, SharedDrowsinessSystem.DrowsinessKey, out var time, status))
+ TimeSpan? remainingTime = TimeSpan.Zero;
+ foreach (var (_, _, statusEffectComp) in drowsinessEffects)
+ {
+ if (statusEffectComp.EndEffectTime > remainingTime)
+ remainingTime = statusEffectComp.EndEffectTime;
+ }
+
+ if (remainingTime is null)
return;
var curTime = _timing.CurTime;
- var timeLeft = (float)(time.Value.Item2 - curTime).TotalSeconds;
+ var timeLeft = (float)(remainingTime - curTime).Value.TotalSeconds;
CurrentPower += 8f * (0.5f * timeLeft - CurrentPower) * args.DeltaSeconds / (timeLeft + 1);
}
using Content.Shared.Drowsiness;
+using Content.Shared.StatusEffectNew;
using Robust.Client.Graphics;
using Robust.Client.Player;
-using Robust.Shared.Player;
namespace Content.Client.Drowsiness;
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
+ [Dependency] private readonly SharedStatusEffectsSystem _statusEffects = default!;
private DrowsinessOverlay _overlay = default!;
{
base.Initialize();
- SubscribeLocalEvent<DrowsinessComponent, ComponentInit>(OnDrowsinessInit);
- SubscribeLocalEvent<DrowsinessComponent, ComponentShutdown>(OnDrowsinessShutdown);
+ SubscribeLocalEvent<DrowsinessStatusEffectComponent, StatusEffectAppliedEvent>(OnDrowsinessApply);
+ SubscribeLocalEvent<DrowsinessStatusEffectComponent, StatusEffectRemovedEvent>(OnDrowsinessShutdown);
- SubscribeLocalEvent<DrowsinessComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
- SubscribeLocalEvent<DrowsinessComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
+ SubscribeLocalEvent<DrowsinessStatusEffectComponent, StatusEffectPlayerAttachedEvent>(OnStatusEffectPlayerAttached);
+ SubscribeLocalEvent<DrowsinessStatusEffectComponent, StatusEffectPlayerDetachedEvent>(OnStatusEffectPlayerDetached);
_overlay = new();
}
- private void OnPlayerAttached(EntityUid uid, DrowsinessComponent component, LocalPlayerAttachedEvent args)
+ private void OnDrowsinessApply(Entity<DrowsinessStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
{
- _overlayMan.AddOverlay(_overlay);
+ if (_player.LocalEntity == args.Target)
+ _overlayMan.AddOverlay(_overlay);
}
- private void OnPlayerDetached(EntityUid uid, DrowsinessComponent component, LocalPlayerDetachedEvent args)
+ private void OnDrowsinessShutdown(Entity<DrowsinessStatusEffectComponent> ent, ref StatusEffectRemovedEvent args)
{
- _overlay.CurrentPower = 0;
- _overlayMan.RemoveOverlay(_overlay);
+ if (_player.LocalEntity != args.Target)
+ return;
+
+ if (!_statusEffects.HasEffectComp<DrowsinessStatusEffectComponent>(_player.LocalEntity.Value))
+ {
+ _overlay.CurrentPower = 0;
+ _overlayMan.RemoveOverlay(_overlay);
+ }
}
- private void OnDrowsinessInit(EntityUid uid, DrowsinessComponent component, ComponentInit args)
+ private void OnStatusEffectPlayerAttached(Entity<DrowsinessStatusEffectComponent> ent, ref StatusEffectPlayerAttachedEvent args)
{
- if (_player.LocalEntity == uid)
- _overlayMan.AddOverlay(_overlay);
+ if (_player.LocalEntity != args.Target)
+ return;
+
+ _overlayMan.AddOverlay(_overlay);
}
- private void OnDrowsinessShutdown(EntityUid uid, DrowsinessComponent component, ComponentShutdown args)
+ private void OnStatusEffectPlayerDetached(Entity<DrowsinessStatusEffectComponent> ent, ref StatusEffectPlayerDetachedEvent args)
{
- if (_player.LocalEntity == uid)
+ if (_player.LocalEntity != args.Target)
+ return;
+
+ if (!_statusEffects.HasEffectComp<DrowsinessStatusEffectComponent>(_player.LocalEntity.Value))
{
_overlay.CurrentPower = 0;
_overlayMan.RemoveOverlay(_overlay);
--- /dev/null
+using Content.Shared.StatusEffectNew;
+using Content.Shared.StatusEffectNew.Components;
+using Robust.Shared.Collections;
+using Robust.Shared.GameStates;
+
+namespace Content.Client.StatusEffectNew;
+
+/// <inheritdoc/>
+public sealed partial class ClientStatusEffectsSystem : SharedStatusEffectsSystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<StatusEffectContainerComponent, ComponentHandleState>(OnHandleState);
+ }
+
+ private void OnHandleState(Entity<StatusEffectContainerComponent> ent, ref ComponentHandleState args)
+ {
+ if (args.Current is not StatusEffectContainerComponentState state)
+ return;
+
+ var toRemove = new ValueList<EntityUid>();
+ foreach (var effect in ent.Comp.ActiveStatusEffects)
+ {
+ if (state.ActiveStatusEffects.Contains(GetNetEntity(effect)))
+ continue;
+
+ toRemove.Add(effect);
+ }
+
+ foreach (var effect in toRemove)
+ {
+ ent.Comp.ActiveStatusEffects.Remove(effect);
+ var ev = new StatusEffectRemovedEvent(ent);
+ RaiseLocalEvent(effect, ref ev);
+ }
+
+ foreach (var effect in state.ActiveStatusEffects)
+ {
+ var effectUid = GetEntity(effect);
+ if (ent.Comp.ActiveStatusEffects.Contains(effectUid))
+ continue;
+
+ ent.Comp.ActiveStatusEffects.Add(effectUid);
+ var ev = new StatusEffectAppliedEvent(ent);
+ RaiseLocalEvent(effectUid, ref ev);
+ }
+ }
+}
-using Content.Shared.Bed.Sleep;
+using Content.Server.StatusEffectNew;
+using Content.Shared.Bed.Sleep;
using Content.Shared.Drowsiness;
-using Content.Shared.StatusEffect;
+using Content.Shared.StatusEffectNew;
+using Content.Shared.StatusEffectNew.Components;
using Robust.Shared.Random;
using Robust.Shared.Timing;
public sealed class DrowsinessSystem : SharedDrowsinessSystem
{
- [ValidatePrototypeId<StatusEffectPrototype>]
- private const string SleepKey = "ForcedSleep"; // Same one used by N2O and other sleep chems.
-
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
/// <inheritdoc/>
public override void Initialize()
{
- SubscribeLocalEvent<DrowsinessComponent, ComponentStartup>(OnInit);
+ SubscribeLocalEvent<DrowsinessStatusEffectComponent, StatusEffectAppliedEvent>(OnEffectApplied);
}
- private void OnInit(EntityUid uid, DrowsinessComponent component, ComponentStartup args)
+ private void OnEffectApplied(Entity<DrowsinessStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
{
- component.NextIncidentTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(component.TimeBetweenIncidents.X, component.TimeBetweenIncidents.Y));
+ ent.Comp.NextIncidentTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(ent.Comp.TimeBetweenIncidents.X, ent.Comp.TimeBetweenIncidents.Y));
}
+
public override void Update(float frameTime)
{
base.Update(frameTime);
- var query = EntityQueryEnumerator<DrowsinessComponent>();
- while (query.MoveNext(out var uid, out var component))
+ var query = EntityQueryEnumerator<DrowsinessStatusEffectComponent, StatusEffectComponent>();
+ while (query.MoveNext(out var uid, out var drowsiness, out var statusEffect))
{
- if (_timing.CurTime < component.NextIncidentTime)
+ if (_timing.CurTime < drowsiness.NextIncidentTime)
+ continue;
+
+ if (statusEffect.AppliedTo is null)
continue;
// Set the new time.
- component.NextIncidentTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(component.TimeBetweenIncidents.X, component.TimeBetweenIncidents.Y));
+ drowsiness.NextIncidentTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(drowsiness.TimeBetweenIncidents.X, drowsiness.TimeBetweenIncidents.Y));
// sleep duration
- var duration = TimeSpan.FromSeconds(_random.NextFloat(component.DurationOfIncident.X, component.DurationOfIncident.Y));
+ var duration = TimeSpan.FromSeconds(_random.NextFloat(drowsiness.DurationOfIncident.X, drowsiness.DurationOfIncident.Y));
// Make sure the sleep time doesn't cut into the time to next incident.
- component.NextIncidentTime += duration;
+ drowsiness.NextIncidentTime += duration;
- _statusEffects.TryAddStatusEffect<ForcedSleepingComponent>(uid, SleepKey, duration, false);
+ _statusEffects.TryAddStatusEffect(statusEffect.AppliedTo.Value, SleepingSystem.StatusEffectForcedSleeping, duration);
}
}
}
--- /dev/null
+using Content.Shared.StatusEffectNew;
+using Content.Shared.StatusEffectNew.Components;
+
+namespace Content.Server.StatusEffectNew;
+
+/// <inheritdoc/>
+public sealed partial class StatusEffectsSystem : SharedStatusEffectsSystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<StatusEffectContainerComponent, ComponentShutdown>(OnContainerShutdown);
+ }
+
+ private void OnContainerShutdown(Entity<StatusEffectContainerComponent> ent, ref ComponentShutdown args)
+ {
+ foreach (var effect in ent.Comp.ActiveStatusEffects)
+ {
+ QueueDel(effect);
+ }
+ }
+}
+using Content.Server.StatusEffectNew;
using Content.Shared.Bed.Sleep;
-using Content.Shared.StatusEffect;
using Robust.Shared.Random;
namespace Content.Server.Traits.Assorted;
/// </summary>
public sealed class NarcolepsySystem : EntitySystem
{
- [ValidatePrototypeId<StatusEffectPrototype>]
- private const string StatusEffectKey = "ForcedSleep"; // Same one used by N2O and other sleep chems.
-
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly IRobustRandom _random = default!;
// Make sure the sleep time doesn't cut into the time to next incident.
narcolepsy.NextIncidentTime += duration;
- _statusEffects.TryAddStatusEffect<ForcedSleepingComponent>(uid, StatusEffectKey,
- TimeSpan.FromSeconds(duration), false);
+ _statusEffects.TryAddStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping, TimeSpan.FromSeconds(duration));
}
}
}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Bed.Sleep
-{
- /// <summary>
- /// Prevents waking up. Use as a status effect.
- /// </summary>
- [NetworkedComponent, RegisterComponent]
- public sealed partial class ForcedSleepingComponent : Component
- {}
-}
--- /dev/null
+using Content.Shared.StatusEffectNew.Components;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Bed.Sleep;
+
+/// <summary>
+/// Prevents waking up. Use only in conjunction with <see cref="StatusEffectComponent"/>, on the status effect entity.
+/// </summary>
+[NetworkedComponent, RegisterComponent]
+public sealed partial class ForcedSleepingStatusEffectComponent : Component;
using Content.Shared.Sound.Components;
using Content.Shared.Speech;
using Content.Shared.StatusEffect;
+using Content.Shared.StatusEffectNew;
using Content.Shared.Stunnable;
using Content.Shared.Traits.Assorted;
using Content.Shared.Verbs;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedEmitSoundSystem _emitSound = default!;
- [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
+ [Dependency] private readonly StatusEffectsSystem _statusEffectOld = default!;
+ [Dependency] private readonly SharedStatusEffectsSystem _statusEffectNew = default!;
public static readonly EntProtoId SleepActionId = "ActionSleep";
public static readonly EntProtoId WakeActionId = "ActionWake";
+ public static readonly EntProtoId StatusEffectForcedSleeping = "StatusEffectForcedSleeping";
public override void Initialize()
{
SubscribeLocalEvent<SleepingComponent, GetVerbsEvent<AlternativeVerb>>(AddWakeVerb);
SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand);
- SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit);
+ SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
SubscribeLocalEvent<SleepingComponent, EmoteAttemptEvent>(OnEmoteAttempt);
if (args.FellAsleep)
{
// Expiring status effects would remove the components needed for sleeping
- _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "Stun");
- _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "KnockedDown");
+ _statusEffectOld.TryRemoveStatusEffect(ent.Owner, "Stun");
+ _statusEffectOld.TryRemoveStatusEffect(ent.Owner, "KnockedDown");
EnsureComp<StunnedComponent>(ent);
EnsureComp<KnockedDownComponent>(ent);
_emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive);
}
- private void OnInit(Entity<ForcedSleepingComponent> ent, ref ComponentInit args)
+ private void OnStatusEffectApplied(Entity<ForcedSleepingStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
{
- TrySleeping(ent.Owner);
+ TrySleeping(args.Target);
}
private void Wake(Entity<SleepingComponent> ent)
if (!Resolve(ent, ref ent.Comp, false))
return false;
- if (!force && HasComp<ForcedSleepingComponent>(ent))
+ if (!force && _statusEffectNew.HasEffectComp<ForcedSleepingStatusEffectComponent>(ent))
{
if (user != null)
{
using Content.Shared.Destructible;
using Content.Shared.Rejuvenate;
using Content.Shared.Slippery;
-using Content.Shared.StatusEffect;
+using Content.Shared.StatusEffectNew;
namespace Content.Shared.Damage.Systems;
using System.Numerics;
+using Content.Shared.StatusEffectNew.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Drowsiness;
/// <summary>
-/// Exists for use as a status effect. Adds a shader to the client that scales with the effect duration.
+/// Exists for use as a status effect. Adds a shader to the client that scales with the effect duration.
+/// Use only in conjunction with <see cref="StatusEffectComponent"/>, on the status effect entity.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
-public sealed partial class DrowsinessComponent : Component
+public sealed partial class DrowsinessStatusEffectComponent : Component
{
/// <summary>
/// The random time between sleeping incidents, (min, max).
/// </summary>
- [DataField(required: true)]
- public Vector2 TimeBetweenIncidents = new Vector2(5f, 60f);
+ [DataField]
+ public Vector2 TimeBetweenIncidents = new(5f, 60f);
/// <summary>
/// The duration of sleeping incidents, (min, max).
/// </summary>
- [DataField(required: true)]
- public Vector2 DurationOfIncident = new Vector2(2, 5);
+ [DataField]
+ public Vector2 DurationOfIncident = new(2, 5);
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
-using Content.Shared.StatusEffect;
-
namespace Content.Shared.Drowsiness;
public abstract class SharedDrowsinessSystem : EntitySystem
{
- [ValidatePrototypeId<StatusEffectPrototype>]
- public const string DrowsinessKey = "Drowsiness";
}
/// <remarks>
/// Can be used for things like adding accents or something. I don't know. Go wild.
/// </remarks>
+[Obsolete("Use ModifyStatusEffect with StatusEffectNewSystem instead")]
public sealed partial class GenericStatusEffect : EntityEffect
{
[DataField(required: true)]
--- /dev/null
+using Content.Shared.StatusEffectNew;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.StatusEffects;
+
+/// <summary>
+/// Changes status effects on entities: Adds, removes or sets time.
+/// </summary>
+[UsedImplicitly]
+public sealed partial class ModifyStatusEffect : EntityEffect
+{
+ [DataField(required: true)]
+ public EntProtoId EffectProto;
+
+ /// <summary>
+ /// Time for which status effect should be applied. Behaviour changes according to <see cref="Refresh" />.
+ /// </summary>
+ [DataField]
+ public float Time = 2.0f;
+
+ /// <remarks>
+ /// true - refresh status effect time, false - accumulate status effect time.
+ /// </remarks>
+ [DataField]
+ public bool Refresh = true;
+
+ /// <summary>
+ /// Should this effect add the status effect, remove time from it, or set its cooldown?
+ /// </summary>
+ [DataField]
+ public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
+
+ /// <inheritdoc />
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem<SharedStatusEffectsSystem>();
+
+ var time = Time;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ time *= reagentArgs.Scale.Float();
+
+ switch (Type)
+ {
+ case StatusEffectMetabolismType.Add:
+ statusSys.TryAddStatusEffect(args.TargetEntity, EffectProto, TimeSpan.FromSeconds(time), Refresh);
+ break;
+ case StatusEffectMetabolismType.Remove:
+ statusSys.TryAddTime(args.TargetEntity, EffectProto, -TimeSpan.FromSeconds(time));
+ break;
+ case StatusEffectMetabolismType.Set:
+ statusSys.TrySetTime(args.TargetEntity, EffectProto, TimeSpan.FromSeconds(time));
+ break;
+ }
+ }
+
+ /// <inheritdoc />
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString(
+ "reagent-effect-guidebook-status-effect",
+ ("chance", Probability),
+ ("type", Type),
+ ("time", Time),
+ ("key", prototype.Index(EffectProto).Name)
+ );
+}
using Content.Shared.Bed.Sleep;
using Content.Shared.CCVar;
+using Content.Shared.StatusEffectNew;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Timing;
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedStatusEffectsSystem _statusEffects = default!;
private bool _icSsdSleep;
private float _icSsdSleepTime;
component.FallAsleepTime = TimeSpan.Zero;
if (component.ForcedSleepAdded) // Remove component only if it has been added by this system
{
- EntityManager.RemoveComponent<ForcedSleepingComponent>(uid);
+ _statusEffects.TryRemoveStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping);
component.ForcedSleepAdded = false;
}
}
+
Dirty(uid, component);
}
{
component.FallAsleepTime = _timing.CurTime + TimeSpan.FromSeconds(_icSsdSleepTime);
}
+
Dirty(uid, component);
}
while (query.MoveNext(out var uid, out var ssd))
{
// Forces the entity to sleep when the time has come
- if(ssd.IsSSD &&
+ if (ssd.IsSSD &&
ssd.FallAsleepTime <= _timing.CurTime &&
- !TerminatingOrDeleted(uid) &&
- !HasComp<ForcedSleepingComponent>(uid)) // Don't add the component if the entity has it from another sources
+ !TerminatingOrDeleted(uid))
{
- EnsureComp<ForcedSleepingComponent>(uid);
+ _statusEffects.TryAddStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping);
ssd.ForcedSleepAdded = true;
}
}
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Alert;
using Content.Shared.Rejuvenate;
+using Content.Shared.StatusEffectNew;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Shared.StatusEffect
{
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public sealed class StatusEffectsSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
/// <param name="status">The status effects component to change, if you already have it.</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>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryAddStatusEffect<T>(EntityUid uid, string key, TimeSpan time, bool refresh,
StatusEffectsComponent? status = null)
where T : IComponent, new()
}
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component,
StatusEffectsComponent? status = null)
{
/// 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>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryAddStatusEffect(EntityUid uid,
string key,
TimeSpan time,
/// 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>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryRemoveStatusEffect(EntityUid uid, string key,
StatusEffectsComponent? status = null, bool remComp = true)
{
/// <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>
/// <returns>False if any status effects failed to be removed, true if they all did.</returns>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryRemoveAllStatusEffects(EntityUid uid,
StatusEffectsComponent? status = null)
{
/// <param name="uid">The entity to check on.</param>
/// <param name="key">The status effect ID to check for</param>
/// <param name="status">The status effect component, should you already have it.</param>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool HasStatusEffect(EntityUid uid, string key,
StatusEffectsComponent? status = null)
{
/// <param name="uid">The entity to check on.</param>
/// <param name="key">The status effect ID to check for</param>
/// <param name="status">The status effect component, should you already have it.</param>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool CanApplyEffect(EntityUid uid, string key, StatusEffectsComponent? status = null)
{
// don't log since stuff calling this prolly doesn't care if we don't actually have it
/// <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>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryAddTime(EntityUid uid, string key, TimeSpan time,
StatusEffectsComponent? status = null)
{
/// <param name="key">The status effect to remove time from.</param>
/// <param name="time">The amount of time to add.</param>
/// <param name="status">The status effect component, should you already have it.</param>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryRemoveTime(EntityUid uid, string key, TimeSpan time,
StatusEffectsComponent? status = null)
{
/// <remarks>
/// Not used internally; just sets it itself.
/// </remarks>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TrySetTime(EntityUid uid, string key, TimeSpan time,
StatusEffectsComponent? status = null)
{
/// <param name="time">Out var for the time, if it exists.</param>
/// <param name="status">The status effects component to use, if any.</param>
/// <returns>False if the status effect was not active, true otherwise.</returns>
+ [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")]
public bool TryGetTime(EntityUid uid, string key,
[NotNullWhen(true)] out (TimeSpan, TimeSpan)? time,
StatusEffectsComponent? status = null)
}
}
- /// <summary>
- /// Raised on an entity before a status effect is added to determine if adding it should be cancelled.
- /// </summary>
- [ByRefEvent]
- public record struct BeforeStatusEffectAddedEvent(string Key, bool Cancelled=false);
-
public readonly struct StatusEffectAddedEvent
{
public readonly EntityUid Uid;
--- /dev/null
+using Content.Shared.Alert;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.StatusEffectNew.Components;
+
+/// <summary>
+/// Marker component for all status effects - every status effect entity should have it.
+/// Provides a link between the effect and the affected entity, and some data common to all status effects.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
+[Access(typeof(SharedStatusEffectsSystem))]
+[EntityCategory("StatusEffects")]
+public sealed partial class StatusEffectComponent : Component
+{
+ /// <summary>
+ /// The entity that this status effect is applied to.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public EntityUid? AppliedTo;
+
+ /// <summary>
+ /// Status effect indication for the player. If Null, no Alert will be displayed.
+ /// </summary>
+ [DataField]
+ public ProtoId<AlertPrototype>? Alert;
+
+ /// <summary>
+ /// When this effect will end. If Null, the effect lasts indefinitely.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
+ public TimeSpan? EndEffectTime;
+
+ /// <summary>
+ /// Whitelist, by which it is determined whether this status effect can be imposed on a particular entity.
+ /// </summary>
+ [DataField]
+ public EntityWhitelist? Whitelist;
+
+ /// <summary>
+ /// Blacklist, by which it is determined whether this status effect can be imposed on a particular entity.
+ /// </summary>
+ [DataField]
+ public EntityWhitelist? Blacklist;
+}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.StatusEffectNew.Components;
+
+/// <summary>
+/// Adds container for status effect entities that are applied to entity.
+/// Is applied automatically upon adding any status effect.
+/// Can be used for tracking currently applied status effects.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedStatusEffectsSystem))]
+public sealed partial class StatusEffectContainerComponent : Component
+{
+ [DataField]
+ public HashSet<EntityUid> ActiveStatusEffects = new();
+}
+
+[Serializable, NetSerializable]
+public sealed class StatusEffectContainerComponentState(HashSet<NetEntity> activeStatusEffects) : ComponentState
+{
+ public readonly HashSet<NetEntity> ActiveStatusEffects = activeStatusEffects;
+}
--- /dev/null
+using Content.Shared.Alert;
+using Content.Shared.StatusEffectNew.Components;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+using Robust.Shared.Network;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.StatusEffectNew;
+
+/// <summary>
+/// This system controls status effects, their lifetime, and provides an API for adding them to entities,
+/// removing them from entities, or getting information about current effects on entities.
+/// </summary>
+public abstract partial class SharedStatusEffectsSystem : EntitySystem
+{
+ [Dependency] private readonly AlertsSystem _alerts = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ [Dependency] private readonly IComponentFactory _compFactory = default!;
+ [Dependency] private readonly INetManager _net = default!;
+
+ private EntityQuery<StatusEffectContainerComponent> _containerQuery;
+ private EntityQuery<StatusEffectComponent> _effectQuery;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<StatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
+ SubscribeLocalEvent<StatusEffectComponent, StatusEffectRemovedEvent>(OnStatusEffectRemoved);
+
+ SubscribeLocalEvent<StatusEffectContainerComponent, LocalPlayerAttachedEvent>(OnStatusEffectContainerAttached);
+ SubscribeLocalEvent<StatusEffectContainerComponent, LocalPlayerDetachedEvent>(OnStatusEffectContainerDetached);
+ SubscribeLocalEvent<StatusEffectContainerComponent, ComponentGetState>(OnGetState);
+
+ _containerQuery = GetEntityQuery<StatusEffectContainerComponent>();
+ _effectQuery = GetEntityQuery<StatusEffectComponent>();
+ }
+
+ private void OnGetState(Entity<StatusEffectContainerComponent> ent, ref ComponentGetState args)
+ {
+ args.State = new StatusEffectContainerComponentState(GetNetEntitySet(ent.Comp.ActiveStatusEffects));
+ }
+
+ private void OnStatusEffectContainerAttached(Entity<StatusEffectContainerComponent> ent, ref LocalPlayerAttachedEvent args)
+ {
+ foreach (var effect in ent.Comp.ActiveStatusEffects)
+ {
+ var ev = new StatusEffectPlayerAttachedEvent(ent);
+ RaiseLocalEvent(effect, ref ev);
+ }
+ }
+
+ private void OnStatusEffectContainerDetached(Entity<StatusEffectContainerComponent> ent, ref LocalPlayerDetachedEvent args)
+ {
+ foreach (var effect in ent.Comp.ActiveStatusEffects)
+ {
+ var ev = new StatusEffectPlayerDetachedEvent(ent);
+ RaiseLocalEvent(effect, ref ev);
+ }
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator<StatusEffectComponent>();
+ while (query.MoveNext(out var ent, out var effect))
+ {
+ if (effect.EndEffectTime is null)
+ continue;
+
+ if (!(_timing.CurTime >= effect.EndEffectTime))
+ continue;
+
+ if (effect.AppliedTo is null)
+ continue;
+
+ var meta = MetaData(ent);
+ if (meta.EntityPrototype is null)
+ continue;
+
+ TryRemoveStatusEffect(effect.AppliedTo.Value, meta.EntityPrototype);
+ }
+ }
+
+ private void AddStatusEffectTime(EntityUid effect, TimeSpan delta)
+ {
+ if (!_effectQuery.TryComp(effect, out var effectComp))
+ return;
+
+ effectComp.EndEffectTime += delta;
+ Dirty(effect, effectComp);
+
+ if (effectComp is { AppliedTo: not null, Alert: not null })
+ {
+ (TimeSpan Start, TimeSpan End)? cooldown = effectComp.EndEffectTime is null
+ ? null
+ : (_timing.CurTime, effectComp.EndEffectTime.Value);
+ _alerts.ShowAlert(
+ effectComp.AppliedTo.Value,
+ effectComp.Alert.Value,
+ cooldown: cooldown
+ );
+ }
+ }
+
+ private void SetStatusEffectTime(EntityUid effect, TimeSpan duration)
+ {
+ if (!_effectQuery.TryComp(effect, out var effectComp))
+ return;
+
+ effectComp.EndEffectTime = _timing.CurTime + duration;
+ Dirty(effect, effectComp);
+
+ if (effectComp is { AppliedTo: not null, Alert: not null })
+ {
+ (TimeSpan, TimeSpan)? cooldown = effectComp.EndEffectTime is null
+ ? null
+ : (_timing.CurTime, effectComp.EndEffectTime.Value);
+ _alerts.ShowAlert(
+ effectComp.AppliedTo.Value,
+ effectComp.Alert.Value,
+ cooldown: cooldown
+ );
+ }
+ }
+
+ private void OnStatusEffectApplied(Entity<StatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
+ {
+ if (ent.Comp is { AppliedTo: not null, Alert: not null })
+ {
+ (TimeSpan, TimeSpan)? cooldown = ent.Comp.EndEffectTime is null
+ ? null
+ : (_timing.CurTime, ent.Comp.EndEffectTime.Value);
+ _alerts.ShowAlert(
+ ent.Comp.AppliedTo.Value,
+ ent.Comp.Alert.Value,
+ cooldown: cooldown
+ );
+ }
+ }
+
+ private void OnStatusEffectRemoved(Entity<StatusEffectComponent> ent, ref StatusEffectRemovedEvent args)
+ {
+ if (ent.Comp.AppliedTo is null)
+ return;
+
+ if (ent.Comp is { AppliedTo: not null, Alert: not null })
+ _alerts.ClearAlert(ent.Comp.AppliedTo.Value, ent.Comp.Alert.Value);
+ }
+
+ private bool CanAddStatusEffect(EntityUid uid, EntProtoId effectProto)
+ {
+ if (!_proto.TryIndex(effectProto, out var effectProtoData))
+ return false;
+
+ if (!effectProtoData.TryGetComponent<StatusEffectComponent>(out var effectProtoComp, _compFactory))
+ return false;
+
+ if (!_whitelist.CheckBoth(uid, effectProtoComp.Blacklist, effectProtoComp.Whitelist))
+ return false;
+
+ var ev = new BeforeStatusEffectAddedEvent(effectProto);
+ RaiseLocalEvent(uid, ref ev);
+
+ if (ev.Cancelled)
+ return false;
+
+ return true;
+ }
+}
+
+/// <summary>
+/// Calls on effect entity, when a status effect is applied.
+/// </summary>
+[ByRefEvent]
+public readonly record struct StatusEffectAppliedEvent(EntityUid Target);
+
+/// <summary>
+/// Calls on effect entity, when a status effect is removed.
+/// </summary>
+[ByRefEvent]
+public readonly record struct StatusEffectRemovedEvent(EntityUid Target);
+
+/// <summary>
+/// Called on a status effect entity inside <see cref="StatusEffectContainerComponent"/>
+/// after a player has been <see cref="LocalPlayerAttachedEvent"/> to this container entity.
+/// </summary>
+[ByRefEvent]
+public readonly record struct StatusEffectPlayerAttachedEvent(EntityUid Target);
+
+/// <summary>
+/// Called on a status effect entity inside <see cref="StatusEffectContainerComponent"/>
+/// after a player has been <see cref="LocalPlayerDetachedEvent"/> to this container entity.
+/// </summary>
+[ByRefEvent]
+public readonly record struct StatusEffectPlayerDetachedEvent(EntityUid Target);
+
+/// <summary>
+/// Raised on an entity before a status effect is added to determine if adding it should be cancelled.
+/// </summary>
+[ByRefEvent]
+public record struct BeforeStatusEffectAddedEvent(EntProtoId Effect, bool Cancelled = false);
--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using Content.Shared.StatusEffectNew.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.StatusEffectNew;
+
+public abstract partial class SharedStatusEffectsSystem
+{
+ /// <summary>
+ /// Attempts to add a status effect to the specified entity. Returns True if the effect is added or it already exists
+ /// and has been successfully extended in time, returns False if the status effect cannot be applied to this entity,
+ /// or for any other reason.
+ /// </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="resetCooldown">
+ /// If True, the effect duration time will be reset and reapplied. If False, the effect duration time will be overlaid with the existing one.
+ /// In the other case, the effect will either be added for the specified time or its time will be extended for the specified time.
+ /// </param>
+ public bool TryAddStatusEffect(
+ EntityUid target,
+ EntProtoId effectProto,
+ TimeSpan? duration = null,
+ bool resetCooldown = false
+ )
+ {
+ if (TryGetStatusEffect(target, effectProto, out var existedEffect))
+ {
+ //We don't need to add the effect if it already exists
+ if (duration is null)
+ return true;
+
+ if (resetCooldown)
+ SetStatusEffectTime(existedEffect.Value, duration.Value);
+ else
+ AddStatusEffectTime(existedEffect.Value, duration.Value);
+
+ return true;
+ }
+
+ if (!CanAddStatusEffect(target, effectProto))
+ return false;
+
+ var container = EnsureComp<StatusEffectContainerComponent>(target);
+
+ //And only if all checks passed we spawn the effect
+ var effect = PredictedSpawnAttachedTo(effectProto, Transform(target).Coordinates);
+ _transform.SetParent(effect, target);
+ if (!_effectQuery.TryComp(effect, out var effectComp))
+ return false;
+
+ if (duration != null)
+ effectComp.EndEffectTime = _timing.CurTime + duration;
+
+ container.ActiveStatusEffects.Add(effect);
+ effectComp.AppliedTo = target;
+ Dirty(target, container);
+ Dirty(effect, effectComp);
+
+ var ev = new StatusEffectAppliedEvent(target);
+ RaiseLocalEvent(effect, ref ev);
+
+ return true;
+ }
+
+ /// <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;
+ }
+
+ /// <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)
+ {
+ if (!TryComp<StatusEffectComponent>(effect, out var statusComp))
+ continue;
+
+ if (TryComp<T>(effect, out var comp))
+ {
+ effects ??= [];
+ effects.Add((effect, comp, statusComp));
+ }
+ }
+
+ return effects != null;
+ }
+}
entity-category-name-roles = Mind Roles
entity-category-name-mapping = Mapping
entity-category-name-donotmap = Do not map
+entity-category-name-status-effects = Status Effects
entity-category-suffix-donotmap = DO NOT MAP
- Electrocution
- TemporaryBlindness
- RadiationProtection
- - Drowsiness
- Adrenaline
- type: StandingState
- type: Tag
- SlowedDown
- Stutter
- Electrocution
- - ForcedSleep
- TemporaryBlindness
- Pacified
- Flashed
- RadiationProtection
- - Drowsiness
- Adrenaline
- type: Buckle
- type: StandingState
- SlowedDown
- Stutter
- Electrocution
- - ForcedSleep
- TemporaryBlindness
- Pacified
- StaminaModifier
- Flashed
- RadiationProtection
- - Drowsiness
- Adrenaline
- type: Bloodstream
bloodMaxVolume: 150
- SlowedDown
- Stutter
- Electrocution
- - ForcedSleep
- TemporaryBlindness
- Pacified
- RadiationProtection
- - Drowsiness
- Adrenaline
- type: Temperature
heatDamageThreshold: 800
- RatvarianLanguage
- PressureImmunity
- Muted
- - ForcedSleep
- TemporaryBlindness
- Pacified
- StaminaModifier
- Flashed
- RadiationProtection
- - Drowsiness
- Adrenaline
- type: Body
prototype: Human
--- /dev/null
+- type: entity
+ id: StatusEffectBase
+ abstract: true
+ components:
+ - type: StatusEffect
+ - type: Sprite
+ drawdepth: Effects
+ - type: Tag
+ tags:
+ - HideContextMenu
+
+- type: entity
+ parent: StatusEffectBase
+ id: MobStatusEffectBase
+ abstract: true
+ components:
+ - type: StatusEffect
+ whitelist:
+ components:
+ - MobState
+
+# The creature sleeps so heavily that nothing can wake him up. Not even its own death.
+- type: entity
+ parent: MobStatusEffectBase
+ id: StatusEffectForcedSleeping
+ name: forced sleep
+ components:
+ - type: ForcedSleepingStatusEffect
+
+# Blurs your vision and makes you randomly fall asleep
+- type: entity
+ parent: MobStatusEffectBase
+ id: StatusEffectDrowsiness
+ name: drowsiness
+ components:
+ - type: DrowsinessStatusEffect
id: DoNotMap
name: entity-category-name-donotmap
suffix: entity-category-suffix-donotmap
+
+- type: entityCategory
+ id: StatusEffects
+ name: entity-category-name-status-effects
+ hideSpawnMenu: true
\ No newline at end of file
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
fizziness: 0.25
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
fizziness: 0.15
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
fizziness: 0.15
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
fizziness: 0.25
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
fizziness: 0.15
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
fizziness: 0.25
effects:
- !type:SatiateThirst
factor: 2
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 2.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 2
type: Remove
- !type:AdjustReagent
reagent: Theobromine
effects:
- !type:SatiateThirst
factor: 2
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 2.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 2
type: Remove
- type: reagent
effects:
- !type:SatiateThirst
factor: 2
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 2.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 2
type: Remove
- type: reagent
effects:
- !type:SatiateThirst
factor: 6
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 3.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 3
type: Remove
Poison:
effects:
effects:
- !type:SatiateThirst
factor: 2
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 2.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 2
type: Remove
- type: reagent
effects:
- !type:SatiateThirst
factor: 2
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 1.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 1
type: Remove
- type: reagent
effects:
- !type:SatiateThirst
factor: 2
- - !type:GenericStatusEffect
- key: Drowsiness
- time: 2.0
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
+ time: 2
type: Remove
- !type:AdjustReagent
reagent: Theobromine
state: icon_empty
metamorphicMaxFillLevels: 5
metamorphicFillBaseName: fill-
- metamorphicChangeColor: false
+ metamorphicChangeColor: false
metabolisms:
Drink:
effects:
shouldHave: false
walkSpeedModifier: 0.65
sprintSpeedModifier: 0.65
- - !type:GenericStatusEffect
+ - !type:ModifyStatusEffect
conditions:
- !type:ReagentThreshold
reagent: NitrousOxide
- !type:OrganType
type: Slime
shouldHave: false
- key: ForcedSleep
- component: ForcedSleeping
+ effectProto: StatusEffectForcedSleeping
time: 3
type: Add
- !type:HealthChange
key: Jitter
time: 3.0
type: Remove
- - !type:GenericStatusEffect
- key: Drowsiness
- component: Drowsiness
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
time: 1.5
type: Add
refresh: false
- !type:GenericStatusEffect
key: Stutter
component: StutteringAccent
- - !type:GenericStatusEffect
- key: Drowsiness
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
time: 10
type: Remove
- !type:ResetNarcolepsy
metabolisms:
Medicine:
effects:
- - !type:GenericStatusEffect
- key: Drowsiness
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
time: 10
type: Remove
- !type:ResetNarcolepsy
emote: Yawn
showInChat: true
probability: 0.1
- - !type:GenericStatusEffect
- key: Drowsiness
- component: Drowsiness
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
time: 4
type: Add
refresh: false
key: KnockedDown
time: 3
type: Remove
- - !type:GenericStatusEffect
+ - !type:ModifyStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Haloperidol
max: 0.01
- key: Drowsiness
+ effectProto: StatusEffectDrowsiness
time: 10
type: Remove
Medicine:
key: KnockedDown
time: 1
type: Remove
- - !type:GenericStatusEffect
+ - !type:ModifyStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Haloperidol
max: 0.01
- key: Drowsiness
+ effectProto: StatusEffectDrowsiness
time: 10
type: Remove
- !type:PopupMessage
component: StaminaModifier
time: 3
type: Add
- - !type:GenericStatusEffect
- key: ForcedSleep
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectForcedSleeping
time: 3
type: Remove
- - !type:GenericStatusEffect
+ - !type:ModifyStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Haloperidol
max: 0.01
- key: Drowsiness
+ effectProto: StatusEffectDrowsiness
time: 10
type: Remove
Medicine:
metabolisms:
Narcotic:
effects:
- - !type:GenericStatusEffect
+ - !type:ModifyStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Nocturine
min: 8
- key: ForcedSleep
- component: ForcedSleeping
- refresh: false
+ effectProto: StatusEffectForcedSleeping
+ time: 3
type: Add
- type: reagent
- !type:MovespeedModifier
walkSpeedModifier: 0.65
sprintSpeedModifier: 0.65
- - !type:GenericStatusEffect
- key: Drowsiness
- component: Drowsiness
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectDrowsiness
time: 4
type: Add
refresh: false
# Status effect prototypes.
# Holds no actual logic, just some basic data about the effect.
+# Note: We have a new status effect system that needs all of these status effects to be fully ported to.
+# Adding new status effects under the old system is NOT RECOMMENDED.
+
- type: statusEffect
id: Stun
alert: Stun
id: Corporeal
alert: Corporeal
-- type: statusEffect
- id: ForcedSleep #I.e., they will not wake on damage or similar
-
- type: statusEffect
id: TemporaryBlindness
- type: statusEffect
id: RadiationProtection
-- type: statusEffect
- id: Drowsiness #blurs your vision and makes you randomly fall asleep
-
- type: statusEffect
id: Adrenaline
alert: Adrenaline