]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Tearable Deliveries V2 (#36815)
authorScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Thu, 24 Apr 2025 19:47:46 +0000 (21:47 +0200)
committerGitHub <noreply@github.com>
Thu, 24 Apr 2025 19:47:46 +0000 (21:47 +0200)
* Add new fields to DeliveryComponent for #36636

* Setting the baseSpesoPenalty for currently available deliveries

* Small fixes

* Basic delivery penalization

* Penalty and reward multiplier calculation in place

Also fixes an issue in SharedCargoSystem when opening a delivery in dev server due to trying to allocate cargo twice.

* Calling penalty no longer happens on opening

* Extract multiplier getting

* Removing unused include

* Changing method description. \n\n Not actually sure what I meant by the first one

* Localising default delivery messages

* Unused include removal

* init or smth

* minor tweaks

* I KEEP MERGE CONFLICTING MYSELF

* comments

* no icon

* slight increase

* slarti changes

* forgot

* stuffs

* yippee

* Locn't

* doc

* partial review

* message

* review

* pain

* stuff

---------

Co-authored-by: Lmorgan89 <billsmith116@gmail.com>
Content.Shared/Delivery/SharedDeliverySystem.cs
Content.Shared/FingerprintReader/FingerprintReaderSystem.cs
Content.Shared/Tools/Components/SimpleToolUsageComponent.cs [new file with mode: 0644]
Content.Shared/Tools/Systems/SimpleToolUsageSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/delivery/delivery-component.ftl
Resources/Locale/en-US/tools/simple-tool-usage.ftl [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Deliveries/deliveries.yml

index ffbf520610c4b97b2207277c99711e38c01ddcc5..53c5224940af1689c03fc7f83e3ed846bfe40f68 100644 (file)
@@ -8,6 +8,7 @@ using Content.Shared.Interaction.Events;
 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;
@@ -41,6 +42,8 @@ public abstract class SharedDeliverySystem : EntitySystem
         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);
@@ -100,6 +103,24 @@ public abstract class SharedDeliverySystem : EntitySystem
         });
     }
 
+
+    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)
@@ -127,33 +148,38 @@ public abstract class SharedDeliverySystem : EntitySystem
         });
     }
 
-    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);
 
@@ -170,12 +196,13 @@ public abstract class SharedDeliverySystem : EntitySystem
 
         _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;
@@ -189,7 +216,7 @@ public abstract class SharedDeliverySystem : EntitySystem
         }
         else
         {
-            _container.EmptyContainer(container, true, Transform(ent.Owner).Coordinates);
+            _container.EmptyContainer(container, true);
         }
     }
 
index e259a1773802b45b90915b8ae70d7bfd69e3b053..aa7d190c34c552e3a298ae102e18b60a3e3b344b 100644 (file)
@@ -19,7 +19,7 @@ public sealed class FingerprintReaderSystem : EntitySystem
     /// <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;
@@ -30,7 +30,7 @@ public sealed class FingerprintReaderSystem : EntitySystem
         // 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;
         }
@@ -39,7 +39,7 @@ public sealed class FingerprintReaderSystem : EntitySystem
         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;
diff --git a/Content.Shared/Tools/Components/SimpleToolUsageComponent.cs b/Content.Shared/Tools/Components/SimpleToolUsageComponent.cs
new file mode 100644 (file)
index 0000000..1f1e9c6
--- /dev/null
@@ -0,0 +1,47 @@
+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;
diff --git a/Content.Shared/Tools/Systems/SimpleToolUsageSystem.cs b/Content.Shared/Tools/Systems/SimpleToolUsageSystem.cs
new file mode 100644 (file)
index 0000000..0f7da9a
--- /dev/null
@@ -0,0 +1,79 @@
+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);
+    }
+}
index fbe4e74937f3869882db6983e0f061d667196548..a6bbc79343cd38c56845a585946ef4692a6be628 100644 (file)
@@ -10,6 +10,7 @@ delivery-opened-others = {CAPITALIZE($recipient)} opened the {$delivery}.
 
 delivery-unlock-verb = Unlock
 delivery-open-verb = Open
+delivery-slice-verb = Slice open
 
 delivery-teleporter-amount-examine =
     { $amount ->
diff --git a/Resources/Locale/en-US/tools/simple-tool-usage.ftl b/Resources/Locale/en-US/tools/simple-tool-usage.ftl
new file mode 100644 (file)
index 0000000..fd6ae49
--- /dev/null
@@ -0,0 +1 @@
+simple-tool-usage-blocked-message = You need a tool that can perform {$quality}!
index b396b20a464174c653db7b032ba8ef3004fd77d1..19789cd7ebd37d1cf7f2b091c824a452c9a2e96a 100644 (file)
@@ -41,6 +41,9 @@
       delivery: !type:Container
   - type: StealTarget
     stealGroup: Mail
+  - type: SimpleToolUsage
+    doAfter: 4
+    usageVerb: delivery-slice-verb
 
 - type: entity
   parent: BaseDelivery
     size: Huge
   - type: Delivery
     baseSpesoReward: 1000
-    baseSpesoPenalty: 500
+    baseSpesoPenalty: 250 # So low due to dept economy splitting all the earnings, but not splitting the penalty.
   - type: Speech
     speechVerb: Robotic
+  - type: SimpleToolUsage
+    doAfter: 6
   - type: EntityTableContainerFill
     containers:
       delivery: !type:NestedSelector
     storedRotation: 90
   - type: Delivery
     baseSpesoReward: 500
-    baseSpesoPenalty: 250
+    baseSpesoPenalty: 125 # So low due to dept economy splitting all the earnings, but not splitting the penalty.
   - type: Speech
     speechVerb: Robotic
   - type: EntityTableContainerFill