+using Content.Shared.DoAfter;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
[DataField("breakOnEmag")]
[AutoNetworkedField]
public bool BreakOnEmag = true;
+
+ /// <summary>
+ /// Amount of do-after time needed to lock the entity.
+ /// </summary>
+ /// <remarks>
+ /// If set to zero, no do-after will be used.
+ /// </remarks>
+ [DataField]
+ [AutoNetworkedField]
+ public TimeSpan LockTime;
+
+ /// <summary>
+ /// Amount of do-after time needed to unlock the entity.
+ /// </summary>
+ /// <remarks>
+ /// If set to zero, no do-after will be used.
+ /// </remarks>
+ [DataField]
+ [AutoNetworkedField]
+ public TimeSpan UnlockTime;
}
/// <summary>
/// </summary>
[ByRefEvent]
public readonly record struct LockToggledEvent(bool Locked);
+
+/// <summary>
+/// Used to lock a lockable entity that has a lock time configured.
+/// </summary>
+/// <seealso cref="LockComponent"/>
+/// <seealso cref="LockSystem"/>
+[Serializable, NetSerializable]
+public sealed partial class LockDoAfter : DoAfterEvent
+{
+ public override DoAfterEvent Clone()
+ {
+ return this;
+ }
+}
+
+/// <summary>
+/// Used to unlock a lockable entity that has an unlock time configured.
+/// </summary>
+/// <seealso cref="LockComponent"/>
+/// <seealso cref="LockSystem"/>
+[Serializable, NetSerializable]
+public sealed partial class UnlockDoAfter : DoAfterEvent
+{
+ public override DoAfterEvent Clone()
+ {
+ return this;
+ }
+}
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
+using Content.Shared.DoAfter;
using Content.Shared.Emag.Systems;
using Content.Shared.Examine;
using Content.Shared.Hands.Components;
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
/// <inheritdoc />
public override void Initialize()
SubscribeLocalEvent<LockComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<LockComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleLockVerb);
SubscribeLocalEvent<LockComponent, GotEmaggedEvent>(OnEmagged);
+ SubscribeLocalEvent<LockComponent, LockDoAfter>(OnDoAfterLock);
+ SubscribeLocalEvent<LockComponent, UnlockDoAfter>(OnDoAfterUnlock);
}
private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args)
/// <summary>
/// Attmempts to lock a given entity
/// </summary>
+ /// <remarks>
+ /// If the lock is set to require a do-after, a true return value only indicates that the do-after started.
+ /// </remarks>
/// <param name="uid">The entity with the lock</param>
/// <param name="user">The person trying to lock it</param>
/// <param name="lockComp"></param>
+ /// <param name="skipDoAfter">If true, skip the required do-after if one is configured.</param>
/// <returns>If locking was successful</returns>
- public bool TryLock(EntityUid uid, EntityUid user, LockComponent? lockComp = null)
+ public bool TryLock(EntityUid uid, EntityUid user, LockComponent? lockComp = null, bool skipDoAfter = false)
{
if (!Resolve(uid, ref lockComp))
return false;
if (!HasUserAccess(uid, user, quiet: false))
return false;
+ if (!skipDoAfter && lockComp.LockTime != TimeSpan.Zero)
+ {
+ return _doAfter.TryStartDoAfter(
+ new DoAfterArgs(EntityManager, user, lockComp.LockTime, new LockDoAfter(), uid, uid)
+ {
+ BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, RequireCanInteract = true,
+ NeedHand = true
+ });
+ }
+
_sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-do-lock-success",
("entityName", Identity.Name(uid, EntityManager))), uid, user);
_audio.PlayPredicted(lockComp.LockSound, uid, user);
/// <summary>
/// Forces a given entity to be unlocked
/// </summary>
+ /// <remarks>
+ /// This does not process do-after times.
+ /// </remarks>
/// <param name="uid">The entity with the lock</param>
/// <param name="user">The person unlocking it. Can be null</param>
/// <param name="lockComp"></param>
/// <summary>
/// Attmempts to unlock a given entity
/// </summary>
+ /// <remarks>
+ /// If the lock is set to require a do-after, a true return value only indicates that the do-after started.
+ /// </remarks>
/// <param name="uid">The entity with the lock</param>
/// <param name="user">The person trying to unlock it</param>
/// <param name="lockComp"></param>
+ /// <param name="skipDoAfter">If true, skip the required do-after if one is configured.</param>
/// <returns>If locking was successful</returns>
- public bool TryUnlock(EntityUid uid, EntityUid user, LockComponent? lockComp = null)
+ public bool TryUnlock(EntityUid uid, EntityUid user, LockComponent? lockComp = null, bool skipDoAfter = false)
{
if (!Resolve(uid, ref lockComp))
return false;
if (!HasUserAccess(uid, user, quiet: false))
return false;
+ if (!skipDoAfter && lockComp.UnlockTime != TimeSpan.Zero)
+ {
+ return _doAfter.TryStartDoAfter(
+ new DoAfterArgs(EntityManager, user, lockComp.LockTime, new UnlockDoAfter(), uid, uid)
+ {
+ BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, RequireCanInteract = true,
+ NeedHand = true
+ });
+ }
+
Unlock(uid, user, lockComp);
return true;
}
RemComp<LockComponent>(uid); //Literally destroys the lock as a tell it was emagged
args.Handled = true;
}
+
+ private void OnDoAfterLock(EntityUid uid, LockComponent component, LockDoAfter args)
+ {
+ if (args.Cancelled)
+ return;
+
+ TryLock(uid, args.User, skipDoAfter: true);
+ }
+
+ private void OnDoAfterUnlock(EntityUid uid, LockComponent component, UnlockDoAfter args)
+ {
+ if (args.Cancelled)
+ return;
+
+ TryUnlock(uid, args.User, skipDoAfter: true);
+ }
}