Add a sleep delay to Nocturine (NewStatusEffect version) (#40231)

* Initial commit

* Minor fix

* Why is this uncommented here? Hmmm

* No wait this can't be here, oops

* Do better time

* Also guidebook

* Review changes

* Add rejuvination, fix mispredicts
This commit is contained in:
SlamBamActionman
2025-09-26 22:05:34 +02:00
committed by GitHub
parent dc3f5e3564
commit c7117f38ac
7 changed files with 179 additions and 33 deletions

View File

@@ -14,6 +14,7 @@ using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Pointing; using Content.Shared.Pointing;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Rejuvenate;
using Content.Shared.Slippery; using Content.Shared.Slippery;
using Content.Shared.Sound; using Content.Shared.Sound;
using Content.Shared.Sound.Components; using Content.Shared.Sound.Components;
@@ -57,7 +58,7 @@ public sealed partial class SleepingSystem : EntitySystem
SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged); SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<SleepingComponent, EntityZombifiedEvent>(OnZombified); SubscribeLocalEvent<SleepingComponent, EntityZombifiedEvent>(OnZombified);
SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged); SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<SleepingComponent, ComponentInit>(OnCompInit);
SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt); SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt); SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt); SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt);
@@ -68,6 +69,7 @@ public sealed partial class SleepingSystem : EntitySystem
SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand); SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<SleepingComponent, StunEndAttemptEvent>(OnStunEndAttempt); SubscribeLocalEvent<SleepingComponent, StunEndAttemptEvent>(OnStunEndAttempt);
SubscribeLocalEvent<SleepingComponent, StandUpAttemptEvent>(OnStandUpAttempt); SubscribeLocalEvent<SleepingComponent, StandUpAttemptEvent>(OnStandUpAttempt);
SubscribeLocalEvent<SleepingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied); SubscribeLocalEvent<ForcedSleepingStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt); SubscribeLocalEvent<SleepingComponent, UnbuckleAttemptEvent>(OnUnbuckleAttempt);
@@ -133,7 +135,7 @@ public sealed partial class SleepingSystem : EntitySystem
RemComp<SpamEmitSoundComponent>(ent); RemComp<SpamEmitSoundComponent>(ent);
} }
private void OnMapInit(Entity<SleepingComponent> ent, ref MapInitEvent args) private void OnCompInit(Entity<SleepingComponent> ent, ref ComponentInit args)
{ {
var ev = new SleepStateChangedEvent(true); var ev = new SleepStateChangedEvent(true);
RaiseLocalEvent(ent, ref ev); RaiseLocalEvent(ent, ref ev);
@@ -185,6 +187,11 @@ public sealed partial class SleepingSystem : EntitySystem
args.Cancelled = true; args.Cancelled = true;
} }
private void OnRejuvenate(Entity<SleepingComponent> ent, ref RejuvenateEvent args)
{
TryWaking((ent.Owner, ent.Comp), true);
}
private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args) private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args)
{ {
if (args.IsInDetailsRange) if (args.IsInDetailsRange)

View File

@@ -19,6 +19,12 @@ public sealed partial class ModifyStatusEffect : EntityEffect
[DataField] [DataField]
public float Time = 2.0f; public float Time = 2.0f;
/// <summary>
/// Delay before the effect starts. If another effect is added with a shorter delay, it takes precedence.
/// </summary>
[DataField]
public float Delay = 0f;
/// <remarks> /// <remarks>
/// true - refresh status effect time (update to greater value), false - accumulate status effect time. /// true - refresh status effect time (update to greater value), false - accumulate status effect time.
/// </remarks> /// </remarks>
@@ -45,22 +51,30 @@ public sealed partial class ModifyStatusEffect : EntityEffect
{ {
case StatusEffectMetabolismType.Add: case StatusEffectMetabolismType.Add:
if (Refresh) if (Refresh)
statusSys.TryUpdateStatusEffectDuration(args.TargetEntity, EffectProto, duration); statusSys.TryUpdateStatusEffectDuration(args.TargetEntity, EffectProto, duration, Delay > 0 ? TimeSpan.FromSeconds(Delay) : null);
else else
statusSys.TryAddStatusEffectDuration(args.TargetEntity, EffectProto, duration); statusSys.TryAddStatusEffectDuration(args.TargetEntity, EffectProto, duration, Delay > 0 ? TimeSpan.FromSeconds(Delay) : null);
break; break;
case StatusEffectMetabolismType.Remove: case StatusEffectMetabolismType.Remove:
statusSys.TryAddTime(args.TargetEntity, EffectProto, -duration); statusSys.TryAddTime(args.TargetEntity, EffectProto, -duration);
break; break;
case StatusEffectMetabolismType.Set: case StatusEffectMetabolismType.Set:
statusSys.TrySetStatusEffectDuration(args.TargetEntity, EffectProto, duration); statusSys.TrySetStatusEffectDuration(args.TargetEntity, EffectProto, duration, TimeSpan.FromSeconds(Delay));
break; break;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
=> Loc.GetString( Delay > 0
? Loc.GetString(
"reagent-effect-guidebook-status-effect-delay",
("chance", Probability),
("type", Type),
("time", Time),
("key", prototype.Index(EffectProto).Name),
("delay", Delay))
: Loc.GetString(
"reagent-effect-guidebook-status-effect", "reagent-effect-guidebook-status-effect",
("chance", Probability), ("chance", Probability),
("type", Type), ("type", Type),

View File

@@ -9,7 +9,7 @@ namespace Content.Shared.StatusEffectNew.Components;
/// Marker component for all status effects - every status effect entity should have it. /// 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. /// Provides a link between the effect and the affected entity, and some data common to all status effects.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause]
[Access(typeof(StatusEffectsSystem))] [Access(typeof(StatusEffectsSystem))]
[EntityCategory("StatusEffects")] [EntityCategory("StatusEffects")]
public sealed partial class StatusEffectComponent : Component public sealed partial class StatusEffectComponent : Component
@@ -20,12 +20,24 @@ public sealed partial class StatusEffectComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public EntityUid? AppliedTo; public EntityUid? AppliedTo;
/// <summary>
/// When this effect will start. Set to Timespan.Zero to start the effect immediately.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
public TimeSpan StartEffectTime;
/// <summary> /// <summary>
/// When this effect will end. If Null, the effect lasts indefinitely. /// When this effect will end. If Null, the effect lasts indefinitely.
/// </summary> /// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField] [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
public TimeSpan? EndEffectTime; public TimeSpan? EndEffectTime;
/// <summary>
/// If true, this status effect has been applied. Used to ensure that <see cref="StatusEffectAppliedEvent"/> only fires once.
/// </summary>
[DataField, AutoNetworkedField]
public bool Applied;
/// <summary> /// <summary>
/// Whitelist, by which it is determined whether this status effect can be imposed on a particular entity. /// Whitelist, by which it is determined whether this status effect can be imposed on a particular entity.
/// </summary> /// </summary>

View File

@@ -13,13 +13,15 @@ public sealed partial class StatusEffectsSystem
/// <param name="target">The target entity to which the effect should be added.</param> /// <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="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="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
/// <param name="delay">The delay of the effect. If a start time already exists, the closest time takes precedence. Leave null for the effect to be instant.</param>
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if it doesn't exist.</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> /// <returns>True if effect exists and its duration is set properly, false in case effect cannot be applied.</returns>
public bool TryAddStatusEffectDuration( public bool TryAddStatusEffectDuration(
EntityUid target, EntityUid target,
EntProtoId effectProto, EntProtoId effectProto,
[NotNullWhen(true)] out EntityUid? statusEffect, [NotNullWhen(true)] out EntityUid? statusEffect,
TimeSpan duration TimeSpan duration,
TimeSpan? delay = null
) )
{ {
if (duration == TimeSpan.Zero) if (duration == TimeSpan.Zero)
@@ -30,18 +32,19 @@ public sealed partial class StatusEffectsSystem
// We check to make sure time is greater than zero here because sometimes you want to use TryAddStatusEffect to remove duration instead... // 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)) if (!TryGetStatusEffect(target, effectProto, out statusEffect))
return TryAddStatusEffect(target, effectProto, out statusEffect, duration); return TryAddStatusEffect(target, effectProto, out statusEffect, duration, delay);
AddStatusEffectTime(statusEffect.Value, duration); AddStatusEffectTime(statusEffect.Value, duration);
UpdateStatusEffectDelay(statusEffect.Value, delay);
return true; return true;
} }
///<inheritdoc cref="TryAddStatusEffectDuration(EntityUid,EntProtoId,out EntityUid?,TimeSpan)"/> ///<inheritdoc cref="TryAddStatusEffectDuration(EntityUid,EntProtoId,out EntityUid?,TimeSpan,TimeSpan?)"/>
public bool TryAddStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan duration) public bool TryAddStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan duration, TimeSpan? delay = null)
{ {
return TryAddStatusEffectDuration(target, effectProto, out _, duration); return TryAddStatusEffectDuration(target, effectProto, out _, duration, delay);
} }
/// <summary> /// <summary>
@@ -51,13 +54,15 @@ public sealed partial class StatusEffectsSystem
/// <param name="target">The target entity to which the effect should be added.</param> /// <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="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="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
/// <param name="delay">The delay of the effect. If a start time already exists, the closest time takes precedence. Leave null for the effect to be instant.</param>
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if it doesn't exist.</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> /// <returns>True if effect exists and its duration is set properly, false in case effect cannot be applied.</returns>
public bool TrySetStatusEffectDuration( public bool TrySetStatusEffectDuration(
EntityUid target, EntityUid target,
EntProtoId effectProto, EntProtoId effectProto,
[NotNullWhen(true)] out EntityUid? statusEffect, [NotNullWhen(true)] out EntityUid? statusEffect,
TimeSpan? duration = null TimeSpan? duration = null,
TimeSpan? delay = null
) )
{ {
if (duration <= TimeSpan.Zero) if (duration <= TimeSpan.Zero)
@@ -67,17 +72,22 @@ public sealed partial class StatusEffectsSystem
} }
if (!TryGetStatusEffect(target, effectProto, out statusEffect)) if (!TryGetStatusEffect(target, effectProto, out statusEffect))
return TryAddStatusEffect(target, effectProto, out statusEffect, duration); return TryAddStatusEffect(target, effectProto, out statusEffect, duration, delay);
SetStatusEffectEndTime(statusEffect.Value, duration); if (!_effectQuery.TryComp(statusEffect, out var statusEffectComponent))
return false;
var endTime = delay == null || statusEffectComponent.Applied ? _timing.CurTime + duration : _timing.CurTime + delay + duration;
SetStatusEffectEndTime(statusEffect.Value, endTime);
UpdateStatusEffectDelay(statusEffect.Value, delay);
return true; return true;
} }
/// <inheritdoc cref="TrySetStatusEffectDuration(EntityUid,EntProtoId,out EntityUid?,TimeSpan?)"/> /// <inheritdoc cref="TrySetStatusEffectDuration(EntityUid,EntProtoId,out EntityUid?,TimeSpan?,TimeSpan?)"/>
public bool TrySetStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null) public bool TrySetStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null, TimeSpan? delay = null)
{ {
return TrySetStatusEffectDuration(target, effectProto, out _, duration); return TrySetStatusEffectDuration(target, effectProto, out _, duration, delay);
} }
/// <summary> /// <summary>
@@ -87,13 +97,15 @@ public sealed partial class StatusEffectsSystem
/// <param name="target">The target entity to which the effect should be added.</param> /// <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="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="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
/// <param name="delay">The delay of the effect. If a start time already exists, the closest time takes precedence. Leave null for the effect to be instant.</param>
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if it doesn't exist.</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> /// <returns>True if effect exists and its duration is set properly, false in case effect cannot be applied.</returns>
public bool TryUpdateStatusEffectDuration( public bool TryUpdateStatusEffectDuration(
EntityUid target, EntityUid target,
EntProtoId effectProto, EntProtoId effectProto,
[NotNullWhen(true)] out EntityUid? statusEffect, [NotNullWhen(true)] out EntityUid? statusEffect,
TimeSpan? duration = null TimeSpan? duration = null,
TimeSpan? delay = null
) )
{ {
if (duration <= TimeSpan.Zero) if (duration <= TimeSpan.Zero)
@@ -103,17 +115,22 @@ public sealed partial class StatusEffectsSystem
} }
if (!TryGetStatusEffect(target, effectProto, out statusEffect)) if (!TryGetStatusEffect(target, effectProto, out statusEffect))
return TryAddStatusEffect(target, effectProto, out statusEffect, duration); return TryAddStatusEffect(target, effectProto, out statusEffect, duration, delay);
UpdateStatusEffectTime(statusEffect.Value, duration); if (!_effectQuery.TryComp(statusEffect, out var statusEffectComponent))
return false;
var endTime = delay == null || statusEffectComponent.Applied ? duration : delay + duration;
UpdateStatusEffectTime(statusEffect.Value, endTime);
UpdateStatusEffectDelay(statusEffect.Value, delay);
return true; return true;
} }
/// <inheritdoc cref="TryUpdateStatusEffectDuration(EntityUid,EntProtoId,out EntityUid?,TimeSpan?)"/> /// <inheritdoc cref="TryUpdateStatusEffectDuration(EntityUid,EntProtoId,out EntityUid?,TimeSpan?,TimeSpan?)"/>
public bool TryUpdateStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null) public bool TryUpdateStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null, TimeSpan? delay = null)
{ {
return TryUpdateStatusEffectDuration(target, effectProto, out _, duration); return TryUpdateStatusEffectDuration(target, effectProto, out _, duration, delay);
} }
/// <summary> /// <summary>
@@ -193,7 +210,7 @@ public sealed partial class StatusEffectsSystem
public bool TryGetTime( public bool TryGetTime(
EntityUid uid, EntityUid uid,
EntProtoId effectProto, EntProtoId effectProto,
out (EntityUid EffectEnt, TimeSpan? EndEffectTime) time, out (EntityUid EffectEnt, TimeSpan? EndEffectTime, TimeSpan? StartEffectTime) time,
StatusEffectContainerComponent? container = null StatusEffectContainerComponent? container = null
) )
{ {
@@ -209,7 +226,7 @@ public sealed partial class StatusEffectsSystem
if (!_effectQuery.TryComp(effect, out var effectComp)) if (!_effectQuery.TryComp(effect, out var effectComp))
return false; return false;
time = (effect, effectComp.EndEffectTime); time = (effect, effectComp.EndEffectTime, effectComp.StartEffectTime);
return true; return true;
} }
} }

View File

@@ -46,6 +46,8 @@ public sealed partial class StatusEffectsSystem : EntitySystem
var query = EntityQueryEnumerator<StatusEffectComponent>(); var query = EntityQueryEnumerator<StatusEffectComponent>();
while (query.MoveNext(out var ent, out var effect)) while (query.MoveNext(out var ent, out var effect))
{ {
TryApplyStatusEffect((ent, effect));
if (effect.EndEffectTime is null) if (effect.EndEffectTime is null)
continue; continue;
@@ -88,9 +90,6 @@ public sealed partial class StatusEffectsSystem : EntitySystem
statusComp.AppliedTo = ent; statusComp.AppliedTo = ent;
Dirty(args.Entity, statusComp); Dirty(args.Entity, statusComp);
} }
var ev = new StatusEffectAppliedEvent(ent);
RaiseLocalEvent(args.Entity, ref ev);
} }
private void OnEntityRemoved(Entity<StatusEffectContainerComponent> ent, ref EntRemovedFromContainerMessage args) private void OnEntityRemoved(Entity<StatusEffectContainerComponent> ent, ref EntRemovedFromContainerMessage args)
@@ -121,6 +120,29 @@ public sealed partial class StatusEffectsSystem : EntitySystem
PredictedQueueDel(ent.Owner); PredictedQueueDel(ent.Owner);
} }
/// <summary>
/// Applies the status effect, i.e. starts it after it has been added. Ensures delayed start times trigger when they should.
/// </summary>
/// <param name="statusEffectEnt">The status effect entity.</param>
/// <returns>Returns true if the effect is applied.</returns>
private bool TryApplyStatusEffect(Entity<StatusEffectComponent> statusEffectEnt)
{
if (!statusEffectEnt.Comp.Applied &&
statusEffectEnt.Comp.AppliedTo != null &&
_timing.CurTime >= statusEffectEnt.Comp.StartEffectTime)
{
var ev = new StatusEffectAppliedEvent(statusEffectEnt.Comp.AppliedTo.Value);
RaiseLocalEvent(statusEffectEnt, ref ev);
statusEffectEnt.Comp.Applied = true;
DirtyField(statusEffectEnt, statusEffectEnt.Comp, nameof(StatusEffectComponent.StartEffectTime));
return true;
}
return false;
}
public bool CanAddStatusEffect(EntityUid uid, EntProtoId effectProto) public bool CanAddStatusEffect(EntityUid uid, EntProtoId effectProto)
{ {
if (!_proto.Resolve(effectProto, out var effectProtoData)) if (!_proto.Resolve(effectProto, out var effectProtoData))
@@ -148,12 +170,14 @@ public sealed partial class StatusEffectsSystem : EntitySystem
/// <param name="target">The target entity to which the effect should be added.</param> /// <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="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="duration">Duration of status effect. Leave null and the effect will be permanent until it is removed using <c>TryRemoveStatusEffect</c>.</param>
/// <param name="delay">The delay of the effect. Leave null and the effect will be immediate.</param>
/// <param name="statusEffect">The EntityUid of the status effect we have just created or null if we couldn't create one.</param> /// <param name="statusEffect">The EntityUid of the status effect we have just created or null if we couldn't create one.</param>
private bool TryAddStatusEffect( private bool TryAddStatusEffect(
EntityUid target, EntityUid target,
EntProtoId effectProto, EntProtoId effectProto,
[NotNullWhen(true)] out EntityUid? statusEffect, [NotNullWhen(true)] out EntityUid? statusEffect,
TimeSpan? duration = null TimeSpan? duration = null,
TimeSpan? delay = null
) )
{ {
statusEffect = null; statusEffect = null;
@@ -177,7 +201,13 @@ public sealed partial class StatusEffectsSystem : EntitySystem
return false; return false;
statusEffect = effect; statusEffect = effect;
SetStatusEffectEndTime((effect.Value, effectComp), _timing.CurTime + duration);
var endTime = delay == null ? _timing.CurTime + duration : _timing.CurTime + delay + duration;
SetStatusEffectEndTime((effect.Value, effectComp), endTime);
var startTime = delay == null ? TimeSpan.Zero : _timing.CurTime + delay.Value;
SetStatusEffectStartTime(effect.Value, startTime);
TryApplyStatusEffect((effect.Value, effectComp));
return true; return true;
} }
@@ -204,6 +234,28 @@ public sealed partial class StatusEffectsSystem : EntitySystem
SetStatusEffectEndTime(effect, newEndTime); SetStatusEffectEndTime(effect, newEndTime);
} }
private void UpdateStatusEffectDelay(Entity<StatusEffectComponent?> effect, TimeSpan? delay)
{
if (!_effectQuery.Resolve(effect, ref effect.Comp))
return;
// It's already started!
if (_timing.CurTime >= effect.Comp.StartEffectTime)
return;
var newStartTime = TimeSpan.Zero;
if (delay is not null)
{
// Don't update time to a smaller timespan...
newStartTime = _timing.CurTime + delay.Value;
if (effect.Comp.StartEffectTime < newStartTime)
return;
}
SetStatusEffectStartTime(effect, newStartTime);
}
private void AddStatusEffectTime(Entity<StatusEffectComponent?> effect, TimeSpan delta) private void AddStatusEffectTime(Entity<StatusEffectComponent?> effect, TimeSpan delta)
{ {
if (!_effectQuery.Resolve(effect, ref effect.Comp)) if (!_effectQuery.Resolve(effect, ref effect.Comp))
@@ -233,7 +285,26 @@ public sealed partial class StatusEffectsSystem : EntitySystem
var ev = new StatusEffectEndTimeUpdatedEvent(appliedTo, endTime); var ev = new StatusEffectEndTimeUpdatedEvent(appliedTo, endTime);
RaiseLocalEvent(ent, ref ev); RaiseLocalEvent(ent, ref ev);
Dirty(ent); DirtyField(ent, ent.Comp, nameof(StatusEffectComponent.EndEffectTime));
}
private void SetStatusEffectStartTime(Entity<StatusEffectComponent?> ent, TimeSpan startTime)
{
if (!_effectQuery.Resolve(ent, ref ent.Comp))
return;
if (ent.Comp.StartEffectTime == startTime)
return;
ent.Comp.StartEffectTime = startTime;
if (ent.Comp.AppliedTo is not { } appliedTo)
return; // Not much we can do!
var ev = new StatusEffectStartTimeUpdatedEvent(appliedTo, startTime);
RaiseLocalEvent(ent, ref ev);
DirtyField(ent, ent.Comp, nameof(StatusEffectComponent.StartEffectTime));
} }
} }
@@ -262,3 +333,11 @@ public record struct BeforeStatusEffectAddedEvent(EntProtoId Effect, bool Cancel
/// <param name="EndTime">The new end time of the status effect, included for convenience.</param> /// <param name="EndTime">The new end time of the status effect, included for convenience.</param>
[ByRefEvent] [ByRefEvent]
public record struct StatusEffectEndTimeUpdatedEvent(EntityUid Target, TimeSpan? EndTime); public record struct StatusEffectEndTimeUpdatedEvent(EntityUid Target, TimeSpan? EndTime);
/// <summary>
/// Raised on an effect entity when its <see cref="StatusEffectComponent.StartEffectTime"/> is updated in any way.
/// </summary>
/// <param name="Target">The entity the effect is attached to.</param>
/// <param name="StartTime">The new start time of the status effect, included for convenience.</param>
[ByRefEvent]
public record struct StatusEffectStartTimeUpdatedEvent(EntityUid Target, TimeSpan? StartTime);

View File

@@ -118,6 +118,22 @@ reagent-effect-guidebook-status-effect =
} {NATURALFIXED($time, 3)} {MANY("second", $time)} of {LOC($key)} } {NATURALFIXED($time, 3)} {MANY("second", $time)} of {LOC($key)}
} }
reagent-effect-guidebook-status-effect-delay =
{ $type ->
[add] { $chance ->
[1] Causes
*[other] cause
} {LOC($key)} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} with accumulation
*[set] { $chance ->
[1] Causes
*[other] cause
} {LOC($key)} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} without accumulation
[remove]{ $chance ->
[1] Removes
*[other] remove
} {NATURALFIXED($time, 3)} {MANY("second", $time)} of {LOC($key)}
} after a {NATURALFIXED($delay, 3)} second delay
reagent-effect-guidebook-set-solution-temperature-effect = reagent-effect-guidebook-set-solution-temperature-effect =
{ $chance -> { $chance ->
[1] Sets [1] Sets

View File

@@ -304,6 +304,7 @@
min: 8 min: 8
effectProto: StatusEffectForcedSleeping effectProto: StatusEffectForcedSleeping
time: 3 time: 3
delay: 6
type: Add type: Add
- type: reagent - type: reagent