]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Priority Deliveries (#36968)
authorScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Sun, 27 Apr 2025 09:13:52 +0000 (11:13 +0200)
committerGitHub <noreply@github.com>
Sun, 27 Apr 2025 09:13:52 +0000 (02:13 -0700)
Content.Shared/ComponentTable/ComponentTableComponent.cs [new file with mode: 0644]
Content.Shared/ComponentTable/SharedComponentTableSystem.cs [new file with mode: 0644]
Content.Shared/Delivery/DeliveryModifierSystem.cs
Content.Shared/Delivery/DeliveryPriorityComponent.cs [new file with mode: 0644]
Content.Shared/Delivery/DeliveryVisuals.cs
Content.Shared/Delivery/SharedDeliverySystem.cs
Content.Shared/Item/SharedItemSystem.cs
Resources/Locale/en-US/delivery/delivery-component.ftl
Resources/Prototypes/Entities/Objects/Deliveries/deliveries.yml

diff --git a/Content.Shared/ComponentTable/ComponentTableComponent.cs b/Content.Shared/ComponentTable/ComponentTableComponent.cs
new file mode 100644 (file)
index 0000000..a402214
--- /dev/null
@@ -0,0 +1,19 @@
+using Content.Shared.EntityTable.EntitySelectors;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.ComponentTable;
+
+/// <summary>
+/// Applies components from entities selected from the table on init.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedComponentTableSystem))]
+public sealed partial class ComponentTableComponent : Component
+{
+    /// <summary>
+    /// The table from which to grab entities.
+    /// ALL components of the grabbed entities will be added to the holder of this component.
+    /// </summary>
+    [DataField(required: true)]
+    public EntityTableSelector Table = default!;
+}
diff --git a/Content.Shared/ComponentTable/SharedComponentTableSystem.cs b/Content.Shared/ComponentTable/SharedComponentTableSystem.cs
new file mode 100644 (file)
index 0000000..cdb1b2d
--- /dev/null
@@ -0,0 +1,33 @@
+using Content.Shared.EntityTable;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.ComponentTable;
+
+/// <summary>
+/// Applies an entity prototype to an entity on map init. Taken from entities inside an EntityTableSelector.
+/// </summary>
+public sealed class SharedComponentTableSystem : EntitySystem
+{
+    [Dependency] private readonly EntityTableSystem _entTable = default!;
+    [Dependency] private readonly IPrototypeManager _proto = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ComponentTableComponent, MapInitEvent>(OnTableInit);
+    }
+
+    private void OnTableInit(Entity<ComponentTableComponent> ent, ref MapInitEvent args)
+    {
+        var spawns = _entTable.GetSpawns(ent.Comp.Table);
+
+        foreach (var entity in spawns)
+        {
+            if (_proto.TryIndex(entity, out var entProto))
+            {
+                EntityManager.AddComponents(ent, entProto.Components);
+            }
+        }
+    }
+}
index 505975732c30906ca0e946b90cc9c9bf6728f5d3..c4ad1bb3a65a76f1fc8a7dd3622dde0590c6f0e5 100644 (file)
@@ -1,4 +1,9 @@
+using Content.Shared.Examine;
+using Content.Shared.GameTicking;
+using Content.Shared.NameModifier.EntitySystems;
 using Robust.Shared.Random;
+using Robust.Shared.Serialization;
+using Robust.Shared.Timing;
 
 namespace Content.Shared.Delivery;
 
@@ -8,6 +13,9 @@ namespace Content.Shared.Delivery;
 public sealed partial class DeliveryModifierSystem : EntitySystem
 {
     [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly NameModifierSystem _nameModifier = default!;
+    [Dependency] private readonly SharedDeliverySystem _delivery = default!;
 
     public override void Initialize()
     {
@@ -15,8 +23,13 @@ public sealed partial class DeliveryModifierSystem : EntitySystem
 
         SubscribeLocalEvent<DeliveryRandomMultiplierComponent, MapInitEvent>(OnRandomMultiplierMapInit);
         SubscribeLocalEvent<DeliveryRandomMultiplierComponent, GetDeliveryMultiplierEvent>(OnGetRandomMultiplier);
+
+        SubscribeLocalEvent<DeliveryPriorityComponent, MapInitEvent>(OnPriorityMapInit);
+        SubscribeLocalEvent<DeliveryPriorityComponent, ExaminedEvent>(OnPriorityExamine);
+        SubscribeLocalEvent<DeliveryPriorityComponent, GetDeliveryMultiplierEvent>(OnGetPriorityMultiplier);
     }
 
+    #region Random
     private void OnRandomMultiplierMapInit(Entity<DeliveryRandomMultiplierComponent> ent, ref MapInitEvent args)
     {
         ent.Comp.CurrentMultiplierOffset = _random.NextFloat(ent.Comp.MinMultiplierOffset, ent.Comp.MaxMultiplierOffset);
@@ -27,4 +40,70 @@ public sealed partial class DeliveryModifierSystem : EntitySystem
     {
         args.AdditiveMultiplier += ent.Comp.CurrentMultiplierOffset;
     }
+    #endregion
+
+    #region Priority
+    private void OnPriorityMapInit(Entity<DeliveryPriorityComponent> ent, ref MapInitEvent args)
+    {
+        ent.Comp.DeliverUntilTime = _timing.CurTime + ent.Comp.DeliveryTime;
+        _delivery.UpdatePriorityVisuals(ent);
+        Dirty(ent);
+    }
+
+    private void OnPriorityExamine(Entity<DeliveryPriorityComponent> ent, ref ExaminedEvent args)
+    {
+        var trueName = _nameModifier.GetBaseName(ent.Owner);
+        var timeLeft = ent.Comp.DeliverUntilTime - _timing.CurTime;
+
+        if (_timing.CurTime < ent.Comp.DeliverUntilTime)
+            args.PushMarkup(Loc.GetString("delivery-priority-examine", ("type", trueName), ("time", timeLeft.ToString("mm\\:ss"))));
+        else
+            args.PushMarkup(Loc.GetString("delivery-priority-expired-examine", ("type", trueName)));
+    }
+
+    private void OnGetPriorityMultiplier(Entity<DeliveryPriorityComponent> ent, ref GetDeliveryMultiplierEvent args)
+    {
+        if (_timing.CurTime < ent.Comp.DeliverUntilTime)
+            args.AdditiveMultiplier += ent.Comp.InTimeMultiplierOffset;
+        else
+            args.AdditiveMultiplier += ent.Comp.ExpiredMultiplierOffset;
+    }
+    #endregion
+
+    #region Update Loops
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        UpdatePriorty(frameTime);
+    }
+
+    private void UpdatePriorty(float frameTime)
+    {
+        var priorityQuery = EntityQueryEnumerator<DeliveryPriorityComponent>();
+        var curTime = _timing.CurTime;
+
+        while (priorityQuery.MoveNext(out var uid, out var priorityData))
+        {
+            if (priorityData.Expired)
+                continue;
+
+            if (priorityData.DeliverUntilTime < curTime)
+            {
+                priorityData.Expired = true;
+                _delivery.UpdatePriorityVisuals((uid, priorityData));
+                Dirty(uid, priorityData);
+
+                var ev = new DeliveryPriorityExpiredEvent();
+                RaiseLocalEvent(uid, ev);
+            }
+        }
+    }
+    #endregion
 }
+
+/// <summary>
+/// Gets raised on a priority delivery when it's timer expires.
+/// </summary>
+[Serializable, NetSerializable]
+public readonly record struct DeliveryPriorityExpiredEvent;
diff --git a/Content.Shared/Delivery/DeliveryPriorityComponent.cs b/Content.Shared/Delivery/DeliveryPriorityComponent.cs
new file mode 100644 (file)
index 0000000..769c6af
--- /dev/null
@@ -0,0 +1,43 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Delivery;
+
+/// <summary>
+/// Component given to deliveries.
+/// Applies a duration before which the delivery must be delivered.
+/// If successful, adds a small multiplier, otherwise removes a small multiplier.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(DeliveryModifierSystem))]
+public sealed partial class DeliveryPriorityComponent : Component
+{
+    /// <summary>
+    /// The highest the random multiplier can go.
+    /// </summary>
+    [DataField]
+    public float InTimeMultiplierOffset = 0.2f;
+
+    /// <summary>
+    /// The lowest the random multiplier can go.
+    /// </summary>
+    [DataField]
+    public float ExpiredMultiplierOffset = -0.1f;
+
+    /// <summary>
+    /// Whether this priority delivery has already ran out of time or not.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Expired;
+
+    /// <summary>
+    /// How much time you get from spawn until the delivery expires.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public TimeSpan DeliveryTime = TimeSpan.FromMinutes(5);
+
+    /// <summary>
+    /// The time by which this has to be delivered.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public TimeSpan DeliverUntilTime;
+}
index 69eb5fda9ea1339f2febcd13c8a8fc554d87fcd5..35391854beaa8b74879cd8cced44891622488375 100644 (file)
@@ -9,11 +9,18 @@ public enum DeliveryVisuals : byte
     IsTrash,
     IsBroken,
     IsFragile,
-    IsPriority,
-    IsPriorityInactive,
+    PriorityState,
     JobIcon,
 }
 
+[Serializable, NetSerializable]
+public enum DeliveryPriorityState : byte
+{
+    Off,
+    Active,
+    Inactive,
+}
+
 [Serializable, NetSerializable]
 public enum DeliverySpawnerVisuals : byte
 {
index 1667035dac70f125b2ecf29dd2d7cc174634e17a..0f67c3459e023af62cde44dfd2531bcd38d038ec 100644 (file)
@@ -69,7 +69,7 @@ public abstract class SharedDeliverySystem : EntitySystem
             var multiplier = GetDeliveryMultiplier(ent);
             var totalSpesos = Math.Round(ent.Comp.BaseSpesoReward * multiplier);
 
-            args.PushMarkup(Loc.GetString("delivery-earnings-examine", ("spesos", totalSpesos)));
+            args.PushMarkup(Loc.GetString("delivery-earnings-examine", ("spesos", totalSpesos)), -1);
         }
     }
 
@@ -238,7 +238,18 @@ public abstract class SharedDeliverySystem : EntitySystem
 
         // If we're trying to unlock, always remove the priority tape
         if (!isLocked)
-            _appearance.SetData(uid, DeliveryVisuals.IsPriority, false);
+            _appearance.SetData(uid, DeliveryVisuals.PriorityState, DeliveryPriorityState.Off);
+    }
+
+    public void UpdatePriorityVisuals(Entity<DeliveryPriorityComponent> ent)
+    {
+        if (!TryComp<DeliveryComponent>(ent, out var delivery))
+            return;
+
+        if (delivery.IsLocked && !delivery.IsOpened)
+        {
+            _appearance.SetData(ent, DeliveryVisuals.PriorityState, ent.Comp.Expired ? DeliveryPriorityState.Inactive : DeliveryPriorityState.Active);
+        }
     }
 
     protected void UpdateDeliverySpawnerVisuals(EntityUid uid, int contents)
@@ -257,7 +268,10 @@ public abstract class SharedDeliverySystem : EntitySystem
         var ev = new GetDeliveryMultiplierEvent();
         RaiseLocalEvent(ent, ref ev);
 
-        return ev.AdditiveMultiplier * ev.MultiplicativeMultiplier;
+        // Ensure the multiplier can never go below 0.
+        var totalMultiplier = Math.Max(ev.AdditiveMultiplier * ev.MultiplicativeMultiplier, 0);
+
+        return totalMultiplier;
     }
 
     protected virtual void GrantSpesoReward(Entity<DeliveryComponent?> ent) { }
index 18a98c7a0c9cbfb2cee9517f55dd2baf3708ee25..34677966f8072e7de92b5639e5159b195b8b60a2 100644 (file)
@@ -141,7 +141,8 @@ public abstract class SharedItemSystem : EntitySystem
     {
         // show at end of message generally
         args.PushMarkup(Loc.GetString("item-component-on-examine-size",
-            ("size", GetItemSizeLocale(component.Size))), priority: -1);
+            ("size", GetItemSizeLocale(component.Size))),
+            priority: -2);
     }
 
     public ItemSizePrototype GetSizePrototype(ProtoId<ItemSizePrototype> id)
index 5903094690dd3157da28428d68a5fc86709bf607..c7f2347da4ceb11cf0cfc0ea29a5222215f84531 100644 (file)
@@ -20,3 +20,8 @@ delivery-teleporter-amount-examine =
     }
 delivery-teleporter-empty = The {$entity} is empty.
 delivery-teleporter-empty-verb = Take mail
+
+
+# modifiers
+delivery-priority-examine = This is a [color=orange]priority {$type}[/color]. You have [color=orange]{$time}[/color] left to deliver it to get a bonus.
+delivery-priority-expired-examine = This is a [color=orange]priority {$type}[/color]. It seems you ran out of time.
index 2ff5e9daf7da30fd96874ac38f552cadf2b99a89..e90a99510ffb66e49b014c58839eaed2ad7e2f55 100644 (file)
         enum.DeliveryVisualLayers.Lock:
           True: { visible: true }
           False: { visible: false }
-      enum.DeliveryVisuals.IsPriority:
+      enum.DeliveryVisuals.PriorityState:
         enum.DeliveryVisualLayers.PriorityTape:
-          True: { visible: true }
-          False: { visible: false }
-      enum.DeliveryVisuals.IsPriorityInactive:
-        enum.DeliveryVisualLayers.PriorityTape:
-          True: { shader: shaded, state: priority_inactive }
-          False: { shader: unshaded, state: priority }
+          Off: { visible: false }
+          Active: { visible: true, shader: unshaded, state: priority }
+          Inactive: { visible: true, shader: shaded, state: priority_inactive }
       enum.DeliveryVisuals.IsBroken:
         enum.DeliveryVisualLayers.Breakage:
           True: { visible: true }
@@ -45,6 +42,9 @@
   - type: SimpleToolUsage
     doAfter: 4
     usageVerb: delivery-slice-verb
+  - type: ComponentTable
+    table: !type:NestedSelector
+      tableId: DeliveryModifierTable
 
 - type: entity
   parent: BaseDelivery
     containers:
       delivery: !type:NestedSelector
         tableId: LetterDeliveryRewards
+
+
+# Modifier Tables
+- type: entityTable
+  id: DeliveryModifierTable
+  table: !type:AllSelector
+    children:
+    - id: DeliveryModifierPriority
+      prob: 0.25
+
+- type: entity
+  id: DeliveryModifierPriority
+  description: Components to add when a delivery is rolled as priority.
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: DeliveryPriority