Files
tbd-station-14/Content.Server/Lock/LockSystem.cs
JustinTime 7d0b06f551 Add ability to resist out of welded lockers and locked closets (#5958)
* Enable lighting of Cigars with IsHotEvent

Subscribed to IsHotEvent in ThrusterSystem, added AfterInteractEvent to SmokingSystem.Cigar which will set SmokableState to Lit if IsHotEvent returns true

This should mean that cigs/Cigars should light on anything hot

* Revert "Enable lighting of Cigars with IsHotEvent"

This reverts commit db896e1f80940892a87d34aa7182b9c605fa53bc.

* Add the ability to kick your way out of welded or locked closets

Added a ResistLocker component/system that hooks into OnRelayMove to begin resisting out of lockers so that players aren't stuck in lockers forever

Also added check to EntityStorageComponent to not print out welded shut message if the entity attempting to open the locker is inside of it.

* Changes based on review

minor formatting changes

make components passed into AttemptResist nullable and resolve them inside of the function

Remove incorrectly named .ftl file

* Changes based on review

Ditched async DoAfter
Used PopupSystem over User.PopupMessage

Added Cancel token to ResistLockerComponent

Subscribed to EntRemovedFromContainer event to cancel DoAfter if the player is removed from a container

* I am in formatting hell, apparently

* Changes based on review

Added TryComp for the EntityStorageComponent instead of the event passed one

* Changes based on review

* Apply suggestions from code review

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>

* Formatting changes based on review

* Shuffle functions around in LockSystem to properly trigger visualizer

Moved all the unlocking logic to a Lock method inside of LockSystem and TryUnlock calls this method if the user passes all of the access checks

* Formatting, replacing Resolve with TryComp and making AttemptResist arguments optional

Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>

* Check if the player is inside THE container not A container

me no read good

* ok ok I give my code is bad

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
2022-01-05 22:18:48 -08:00

187 lines
6.8 KiB
C#

using Content.Server.Access.Components;
using Content.Server.Access.Systems;
using Content.Server.Storage.Components;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Storage;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Player;
namespace Content.Server.Lock
{
/// <summary>
/// Handles (un)locking and examining of Lock components
/// </summary>
[UsedImplicitly]
public class LockSystem : EntitySystem
{
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LockComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<LockComponent, ActivateInWorldEvent>(OnActivated);
SubscribeLocalEvent<LockComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<LockComponent, GetAlternativeVerbsEvent>(AddToggleLockVerb);
}
private void OnStartup(EntityUid uid, LockComponent lockComp, ComponentStartup args)
{
if (EntityManager.TryGetComponent(lockComp.Owner, out AppearanceComponent? appearance))
{
appearance.SetData(StorageVisuals.CanLock, true);
}
}
private void OnActivated(EntityUid uid, LockComponent lockComp, ActivateInWorldEvent args)
{
if (args.Handled)
return;
// Only attempt an unlock by default on Activate
if (lockComp.Locked)
{
TryUnlock(uid, args.User, lockComp);
args.Handled = true;
}
else if (lockComp.LockOnClick)
{
TryLock(uid, args.User, lockComp);
args.Handled = true;
}
}
private void OnExamined(EntityUid uid, LockComponent lockComp, ExaminedEvent args)
{
args.PushText(Loc.GetString(lockComp.Locked
? "lock-comp-on-examined-is-locked"
: "lock-comp-on-examined-is-unlocked",
("entityName", Name: EntityManager.GetComponent<MetaDataComponent>(lockComp.Owner).EntityName)));
}
public bool TryLock(EntityUid uid, EntityUid user, LockComponent? lockComp = null)
{
if (!Resolve(uid, ref lockComp))
return false;
if (!CanToggleLock(uid, user, quiet: false))
return false;
if (!HasUserAccess(uid, user, quiet: false))
return false;
lockComp.Owner.PopupMessage(user, Loc.GetString("lock-comp-do-lock-success", ("entityName",Name: EntityManager.GetComponent<MetaDataComponent>(lockComp.Owner).EntityName)));
lockComp.Locked = true;
if(lockComp.LockSound != null)
{
SoundSystem.Play(Filter.Pvs(lockComp.Owner), lockComp.LockSound.GetSound(), lockComp.Owner, AudioParams.Default.WithVolume(-5));
}
if (EntityManager.TryGetComponent(lockComp.Owner, out AppearanceComponent? appearanceComp))
{
appearanceComp.SetData(StorageVisuals.Locked, true);
}
RaiseLocalEvent(lockComp.Owner, new LockToggledEvent(true));
return true;
}
public void Unlock(EntityUid uid, EntityUid user, LockComponent? lockComp = null)
{
if (!Resolve(uid, ref lockComp))
return;
lockComp.Owner.PopupMessage(user, Loc.GetString("lock-comp-do-unlock-success", ("entityName", Name: EntityManager.GetComponent<MetaDataComponent>(lockComp.Owner).EntityName)));
lockComp.Locked = false;
if (lockComp.UnlockSound != null)
{
SoundSystem.Play(Filter.Pvs(lockComp.Owner), lockComp.UnlockSound.GetSound(), lockComp.Owner, AudioParams.Default.WithVolume(-5));
}
if (EntityManager.TryGetComponent(lockComp.Owner, out AppearanceComponent? appearanceComp))
{
appearanceComp.SetData(StorageVisuals.Locked, false);
}
RaiseLocalEvent(lockComp.Owner, new LockToggledEvent(false));
}
public bool TryUnlock(EntityUid uid, EntityUid user, LockComponent? lockComp = null)
{
if (!Resolve(uid, ref lockComp))
return false;
if (!CanToggleLock(uid, user, quiet: false))
return false;
if (!HasUserAccess(uid, user, quiet: false))
return false;
Unlock(uid, user, lockComp);
return true;
}
/// <summary>
/// Before locking the entity, check whether it's a locker. If is, prevent it from being locked from the inside or while it is open.
/// </summary>
public bool CanToggleLock(EntityUid uid, EntityUid user, EntityStorageComponent? storage = null, bool quiet = true)
{
if (!Resolve(uid, ref storage, logMissing: false))
return true;
// Cannot lock if the entity is currently opened.
if (storage.Open)
return false;
// Cannot (un)lock from the inside. Maybe a bad idea? Security jocks could trap nerds in lockers?
if (storage.Contents.Contains(user))
return false;
return true;
}
private bool HasUserAccess(EntityUid uid, EntityUid user, AccessReaderComponent? reader = null, bool quiet = true)
{
// Not having an AccessComponent means you get free access. woo!
if (!Resolve(uid, ref reader))
return true;
if (!_accessReader.IsAllowed(reader, user))
{
if (!quiet)
reader.Owner.PopupMessage(user, Loc.GetString("lock-comp-has-user-access-fail"));
return false;
}
return true;
}
private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetAlternativeVerbsEvent args)
{
if (!args.CanAccess || !args.CanInteract || !CanToggleLock(uid, args.User))
return;
Verb verb = new();
verb.Act = component.Locked ?
() => TryUnlock(uid, args.User, component) :
() => TryLock(uid, args.User, component);
verb.Text = Loc.GetString(component.Locked ? "toggle-lock-verb-unlock" : "toggle-lock-verb-lock");
// TODO VERB ICONS need padlock open/close icons.
args.Verbs.Add(verb);
}
}
}