using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Objectives.Components;
using Content.Shared.Popups;
+using Content.Shared.Tools.Components;
using Content.Shared.Tag;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
SubscribeLocalEvent<DeliveryComponent, ExaminedEvent>(OnDeliveryExamine);
SubscribeLocalEvent<DeliveryComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<DeliveryComponent, GetVerbsEvent<AlternativeVerb>>(OnGetDeliveryVerbs);
+ SubscribeLocalEvent<DeliveryComponent, AttemptSimpleToolUseEvent>(OnAttemptSimpleToolUse);
+ SubscribeLocalEvent<DeliveryComponent, SimpleToolDoAfterEvent>(OnSimpleToolUse);
SubscribeLocalEvent<DeliverySpawnerComponent, ExaminedEvent>(OnSpawnerExamine);
SubscribeLocalEvent<DeliverySpawnerComponent, GetVerbsEvent<AlternativeVerb>>(OnGetSpawnerVerbs);
});
}
+
+ private void OnAttemptSimpleToolUse(Entity<DeliveryComponent> ent, ref AttemptSimpleToolUseEvent args)
+ {
+ if (ent.Comp.IsOpened || !ent.Comp.IsLocked)
+ args.Cancelled = true;
+ }
+
+ private void OnSimpleToolUse(Entity<DeliveryComponent> ent, ref SimpleToolDoAfterEvent args)
+ {
+ if (ent.Comp.IsOpened || args.Cancelled)
+ return;
+
+ HandlePenalty(ent);
+
+ TryUnlockDelivery(ent, args.User, false, true);
+ OpenDelivery(ent, args.User, false, true);
+ }
+
private void OnGetSpawnerVerbs(Entity<DeliverySpawnerComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
});
}
- private bool TryUnlockDelivery(Entity<DeliveryComponent> ent, EntityUid user, bool rewardMoney = true)
+ private bool TryUnlockDelivery(Entity<DeliveryComponent> ent, EntityUid user, bool rewardMoney = true, bool force = false)
{
// Check fingerprint access if there is a reader on the mail
- if (TryComp<FingerprintReaderComponent>(ent, out var reader) && !_fingerprintReader.IsAllowed((ent, reader), user))
+ if (!force && TryComp<FingerprintReaderComponent>(ent, out var reader) && !_fingerprintReader.IsAllowed((ent, reader), user))
return false;
var deliveryName = _nameModifier.GetBaseName(ent.Owner);
- _audio.PlayPredicted(ent.Comp.UnlockSound, user, user);
+ if (!force)
+ _audio.PlayPredicted(ent.Comp.UnlockSound, user, user);
ent.Comp.IsLocked = false;
UpdateAntiTamperVisuals(ent, ent.Comp.IsLocked);
DirtyField(ent, ent.Comp, nameof(DeliveryComponent.IsLocked));
+ RemCompDeferred<SimpleToolUsageComponent>(ent); // we don't want unlocked mail to still be cuttable
+
var ev = new DeliveryUnlockedEvent(user);
RaiseLocalEvent(ent, ref ev);
if (rewardMoney)
GrantSpesoReward(ent.AsNullable());
- _popup.PopupPredicted(Loc.GetString("delivery-unlocked-self", ("delivery", deliveryName)),
- Loc.GetString("delivery-unlocked-others", ("delivery", deliveryName), ("recipient", Identity.Name(user, EntityManager)), ("possadj", user)), user, user);
+ if (!force)
+ _popup.PopupPredicted(Loc.GetString("delivery-unlocked-self", ("delivery", deliveryName)),
+ Loc.GetString("delivery-unlocked-others", ("delivery", deliveryName), ("recipient", Identity.Name(user, EntityManager)), ("possadj", user)), user, user);
+
return true;
}
- private void OpenDelivery(Entity<DeliveryComponent> ent, EntityUid user, bool attemptPickup = true)
+ private void OpenDelivery(Entity<DeliveryComponent> ent, EntityUid user, bool attemptPickup = true, bool force = false)
{
var deliveryName = _nameModifier.GetBaseName(ent.Owner);
_tag.AddTags(ent, TrashTag, RecyclableTag);
EnsureComp<SpaceGarbageComponent>(ent);
- RemComp<StealTargetComponent>(ent); // opened mail should not count for the objective
+ RemCompDeferred<StealTargetComponent>(ent); // opened mail should not count for the objective
DirtyField(ent.Owner, ent.Comp, nameof(DeliveryComponent.IsOpened));
- _popup.PopupPredicted(Loc.GetString("delivery-opened-self", ("delivery", deliveryName)),
- Loc.GetString("delivery-opened-others", ("delivery", deliveryName), ("recipient", Identity.Name(user, EntityManager)), ("possadj", user)), user, user);
+ if (!force)
+ _popup.PopupPredicted(Loc.GetString("delivery-opened-self", ("delivery", deliveryName)),
+ Loc.GetString("delivery-opened-others", ("delivery", deliveryName), ("recipient", Identity.Name(user, EntityManager)), ("possadj", user)), user, user);
if (!_container.TryGetContainer(ent, ent.Comp.Container, out var container))
return;
}
else
{
- _container.EmptyContainer(container, true, Transform(ent.Owner).Coordinates);
+ _container.EmptyContainer(container, true);
}
}
/// <param name="user">User trying to gain access.</param>
/// <returns>True if access was granted, otherwise false.</returns>
[PublicAPI]
- public bool IsAllowed(Entity<FingerprintReaderComponent?> target, EntityUid user)
+ public bool IsAllowed(Entity<FingerprintReaderComponent?> target, EntityUid user, bool showPopup = true)
{
if (!Resolve(target, ref target.Comp, false))
return true;
// Check for gloves first
if (!target.Comp.IgnoreGloves && TryGetBlockingGloves(user, out var gloves))
{
- if (target.Comp.FailGlovesPopup != null)
+ if (target.Comp.FailGlovesPopup != null && showPopup)
_popup.PopupClient(Loc.GetString(target.Comp.FailGlovesPopup, ("blocker", gloves)), target, user);
return false;
}
if (!TryComp<FingerprintComponent>(user, out var fingerprint) || fingerprint.Fingerprint == null ||
!target.Comp.AllowedFingerprints.Contains(fingerprint.Fingerprint))
{
- if (target.Comp.FailPopup != null)
+ if (target.Comp.FailPopup != null && showPopup)
_popup.PopupClient(Loc.GetString(target.Comp.FailPopup), target, user);
return false;
--- /dev/null
+using Content.Shared.DoAfter;
+using Content.Shared.Tools.Systems;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Tools.Components;
+
+/// <summary>
+/// Component responsible for simple tool interactions.
+/// Using a tool with the correct quality on an entity with this component will start a doAfter and raise events.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SimpleToolUsageSystem))]
+public sealed partial class SimpleToolUsageComponent : Component
+{
+ /// <summary>
+ /// Tool quality required to use a tool on this.
+ /// </summary>
+ [DataField]
+ public ProtoId<ToolQualityPrototype> Quality = "Slicing";
+
+ /// <summary>
+ /// The duration using a tool on this entity will take in seconds.
+ /// </summary>
+ [DataField]
+ public float DoAfter = 5;
+
+ /// <summary>
+ /// What verb should display to allow you to use a tool on this entity.
+ /// If null, no verb will be shown.
+ /// </summary>
+ [DataField]
+ public LocId? UsageVerb;
+
+ /// <summary>
+ /// The message to show when the verb is disabled.
+ /// </summary>
+ [DataField]
+ public LocId BlockedMessage = "simple-tool-usage-blocked-message";
+}
+
+[ByRefEvent]
+public record struct AttemptSimpleToolUseEvent(EntityUid User, bool Cancelled = false);
+
+[Serializable, NetSerializable]
+public sealed partial class SimpleToolDoAfterEvent : SimpleDoAfterEvent;
--- /dev/null
+using Content.Shared.DoAfter;
+using Content.Shared.Interaction;
+using Content.Shared.Tools.Components;
+using Content.Shared.Verbs;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Tools.Systems;
+
+public sealed partial class SimpleToolUsageSystem : EntitySystem
+{
+ [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+ [Dependency] private readonly SharedToolSystem _tools = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<SimpleToolUsageComponent, AfterInteractUsingEvent>(OnAfterInteract);
+ SubscribeLocalEvent<SimpleToolUsageComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
+ }
+
+ private void OnAfterInteract(Entity<SimpleToolUsageComponent> ent, ref AfterInteractUsingEvent args)
+ {
+ if (!args.CanReach || args.Handled)
+ return;
+
+ if (!_tools.HasQuality(args.Used, ent.Comp.Quality))
+ return;
+
+ AttemptToolUsage(ent, args.User, args.Used);
+ }
+
+ public void OnGetInteractionVerbs(Entity<SimpleToolUsageComponent> ent, ref GetVerbsEvent<InteractionVerb> args)
+ {
+ if (ent.Comp.UsageVerb == null)
+ return;
+
+ if (!args.CanAccess || !args.CanInteract)
+ return;
+
+ var disabled = args.Using == null || !_tools.HasQuality(args.Using.Value, ent.Comp.Quality);
+
+ var used = args.Using;
+ var user = args.User;
+
+ InteractionVerb verb = new()
+ {
+ Act = () =>
+ {
+ if (used != null)
+ AttemptToolUsage(ent, user, used.Value);
+ },
+ Disabled = disabled,
+ Message = disabled ? Loc.GetString(ent.Comp.BlockedMessage, ("quality", ent.Comp.Quality)) : null,
+ Text = Loc.GetString(ent.Comp.UsageVerb),
+ };
+
+ args.Verbs.Add(verb);
+ }
+
+ private void AttemptToolUsage(Entity<SimpleToolUsageComponent> ent, EntityUid user, EntityUid tool)
+ {
+ var attemptEv = new AttemptSimpleToolUseEvent(user);
+ RaiseLocalEvent(ent, ref attemptEv);
+
+ if (attemptEv.Cancelled)
+ return;
+
+ var doAfterArgs = new DoAfterArgs(EntityManager, user, ent.Comp.DoAfter, new SimpleToolDoAfterEvent(), ent, tool)
+ {
+ BreakOnDamage = true,
+ BreakOnDropItem = true,
+ BreakOnMove = true,
+ BreakOnHandChange = true,
+ };
+
+ _doAfterSystem.TryStartDoAfter(doAfterArgs);
+ }
+}