UseDelay + ItemCooldown merge (#22502)
This commit is contained in:
@@ -1,28 +1,36 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Timing;
|
||||
|
||||
/// <summary>
|
||||
/// Timer that creates a cooldown each time an object is activated/used
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
/// <remarks>
|
||||
/// Currently it only supports a single delay per entity, this means that for things that have two delay interactions they will share one timer, so this can cause issues. For example, the bible has a delay when opening the storage UI and when applying it's interaction effect, and they share the same delay.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(UseDelaySystem))]
|
||||
public sealed partial class UseDelayComponent : Component
|
||||
{
|
||||
[AutoNetworkedField]
|
||||
public TimeSpan LastUseTime;
|
||||
|
||||
[AutoNetworkedField]
|
||||
public TimeSpan? DelayEndTime;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(1);
|
||||
/// <summary>
|
||||
/// When the delay starts.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
|
||||
public TimeSpan DelayStartTime;
|
||||
|
||||
/// <summary>
|
||||
/// Stores remaining delay pausing (and eventually, serialization).
|
||||
/// When the delay ends.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
|
||||
public TimeSpan DelayEndTime;
|
||||
|
||||
/// <summary>
|
||||
/// Default delay time
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan? RemainingDelay;
|
||||
|
||||
public bool ActiveDelay => DelayEndTime != null;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[AutoNetworkedField]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(1);
|
||||
}
|
||||
|
||||
@@ -1,125 +1,64 @@
|
||||
using Content.Shared.Cooldown;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Timing;
|
||||
|
||||
public sealed class UseDelaySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private HashSet<UseDelayComponent> _activeDelays = new();
|
||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<UseDelayComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
|
||||
SubscribeLocalEvent<UseDelayComponent, EntityPausedEvent>(OnPaused);
|
||||
SubscribeLocalEvent<UseDelayComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
private void OnPaused(EntityUid uid, UseDelayComponent component, ref EntityPausedEvent args)
|
||||
private void OnUnpaused(Entity<UseDelayComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
// This entity just got paused, but wasn't before
|
||||
if (component.DelayEndTime != null)
|
||||
component.RemainingDelay = _gameTiming.CurTime - component.DelayEndTime;
|
||||
|
||||
_activeDelays.Remove(component);
|
||||
Dirty(component);
|
||||
// We got unpaused, resume the delay
|
||||
ent.Comp.DelayStartTime += args.PausedTime;
|
||||
ent.Comp.DelayEndTime += args.PausedTime;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, UseDelayComponent component, ref EntityUnpausedEvent args)
|
||||
public void SetDelay(Entity<UseDelayComponent> ent, TimeSpan delay)
|
||||
{
|
||||
if (component.RemainingDelay == null)
|
||||
if (ent.Comp.Delay == delay)
|
||||
return;
|
||||
|
||||
// We got unpaused, resume the delay/cooldown. Currently this takes for granted that ItemCooldownComponent
|
||||
// handles the pausing on its own. I'm not even gonna check, because I CBF fixing it if it doesn't.
|
||||
component.DelayEndTime = _gameTiming.CurTime + component.RemainingDelay;
|
||||
Dirty(component);
|
||||
_activeDelays.Add(component);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, UseDelayComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (component.DelayEndTime == null)
|
||||
_activeDelays.Remove(component);
|
||||
else
|
||||
_activeDelays.Add(component);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var toRemove = new RemQueue<UseDelayComponent>();
|
||||
var curTime = _gameTiming.CurTime;
|
||||
var mQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
// TODO refactor this to use active components
|
||||
foreach (var delay in _activeDelays)
|
||||
{
|
||||
if (delay.DelayEndTime == null ||
|
||||
curTime > delay.DelayEndTime ||
|
||||
Deleted(delay.Owner, mQuery))
|
||||
{
|
||||
toRemove.Add(delay);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var delay in toRemove)
|
||||
{
|
||||
delay.DelayEndTime = null;
|
||||
_activeDelays.Remove(delay);
|
||||
Dirty(delay);
|
||||
}
|
||||
ent.Comp.Delay += delay;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts tp start a use-delay for some entity. Returns true unless there is already an active delay.
|
||||
/// Returns true if the entity has a currently active UseDelay.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this will always return true if the entity does not have a use delay component, as in that case there
|
||||
/// is no reason to block/prevent an interaction.
|
||||
/// </remarks>
|
||||
public bool BeginDelay(EntityUid uid, UseDelayComponent? component = null)
|
||||
public bool IsDelayed(Entity<UseDelayComponent> ent)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return true;
|
||||
return ent.Comp.DelayEndTime >= _gameTiming.CurTime;
|
||||
}
|
||||
|
||||
if (component.ActiveDelay)
|
||||
/// <summary>
|
||||
/// Cancels the current delay.
|
||||
/// </summary>
|
||||
public void CancelDelay(Entity<UseDelayComponent> ent)
|
||||
{
|
||||
ent.Comp.DelayEndTime = _gameTiming.CurTime;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the UseDelay entirely for this entity if possible.
|
||||
/// </summary>
|
||||
/// <param name="checkDelayed">Check if the entity has an ongoing delay, return false if it does, return true if it does not.</param>
|
||||
public bool TryResetDelay(Entity<UseDelayComponent> ent, bool checkDelayed = false)
|
||||
{
|
||||
if (checkDelayed && IsDelayed(ent))
|
||||
return false;
|
||||
|
||||
DebugTools.Assert(!_activeDelays.Contains(component));
|
||||
_activeDelays.Add(component);
|
||||
|
||||
var currentTime = _gameTiming.CurTime;
|
||||
component.LastUseTime = currentTime;
|
||||
component.DelayEndTime = currentTime + component.Delay;
|
||||
Dirty(uid, component);
|
||||
|
||||
var cooldown = EnsureComp<ItemCooldownComponent>(uid);
|
||||
cooldown.CooldownStart = currentTime;
|
||||
cooldown.CooldownEnd = component.DelayEndTime;
|
||||
var curTime = _gameTiming.CurTime;
|
||||
ent.Comp.DelayStartTime = curTime;
|
||||
ent.Comp.DelayEndTime = curTime - _metadata.GetPauseTime(ent) + ent.Comp.Delay;
|
||||
Dirty(ent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ActiveDelay(EntityUid uid, UseDelayComponent? component = null)
|
||||
{
|
||||
return Resolve(uid, ref component, false) && component.ActiveDelay;
|
||||
}
|
||||
|
||||
public void Cancel(UseDelayComponent component)
|
||||
{
|
||||
component.DelayEndTime = null;
|
||||
_activeDelays.Remove(component);
|
||||
Dirty(component);
|
||||
|
||||
if (TryComp<ItemCooldownComponent>(component.Owner, out var cooldown))
|
||||
{
|
||||
cooldown.CooldownEnd = _gameTiming.CurTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user