]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Improve paradox clone item copying (#35993)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Wed, 26 Mar 2025 16:13:02 +0000 (17:13 +0100)
committerGitHub <noreply@github.com>
Wed, 26 Mar 2025 16:13:02 +0000 (17:13 +0100)
* even better item copying for the paradox clone

* copy paper

* fix

* blacklist implanter

* string.Empty

---------

Co-authored-by: ScarKy0 <scarky0@onet.eu>
Content.Client/Paper/UI/PaperVisualizerSystem.cs
Content.Server/Cloning/CloningSystem.Subscriptions.cs [new file with mode: 0644]
Content.Server/Cloning/CloningSystem.cs
Content.Server/Forensics/Systems/ForensicsSystem.cs
Content.Shared/Cloning/CloningEvents.cs
Content.Shared/Paper/PaperSystem.cs
Resources/Prototypes/Entities/Mobs/Player/clone.yml

index a0d05736adbbf8e0ed1cd567849924b81821c24a..3fd717f8962397f6219d1b934d3d9a5e941584d6 100644 (file)
@@ -11,13 +11,21 @@ public sealed class PaperVisualizerSystem : VisualizerSystem<PaperVisualsCompone
         if (args.Sprite == null)
             return;
 
-        if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status , out var writingStatus, args.Component))
+        if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status, out var writingStatus, args.Component))
             args.Sprite.LayerSetVisible(PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
 
         if (AppearanceSystem.TryGetData<string>(uid, PaperVisuals.Stamp, out var stampState, args.Component))
         {
-            args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
-            args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
+            if (stampState != string.Empty)
+            {
+                args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
+                args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
+            }
+            else
+            {
+                args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, false);
+            }
+
         }
     }
 }
diff --git a/Content.Server/Cloning/CloningSystem.Subscriptions.cs b/Content.Server/Cloning/CloningSystem.Subscriptions.cs
new file mode 100644 (file)
index 0000000..659d9a1
--- /dev/null
@@ -0,0 +1,83 @@
+using Content.Server.Forensics;
+using Content.Shared.Cloning.Events;
+using Content.Shared.Clothing.Components;
+using Content.Shared.FixedPoint;
+using Content.Shared.Labels.Components;
+using Content.Shared.Labels.EntitySystems;
+using Content.Shared.Paper;
+using Content.Shared.Stacks;
+using Content.Shared.Store;
+using Content.Shared.Store.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Cloning;
+
+/// <summary>
+///     The part of item cloning responsible for copying over important components.
+///     This is used for <see cref="CopyItem"/>.
+///     Anything not copied over here gets reverted to the values the item had in its prototype.
+/// </summary>
+/// <remarks>
+///     This method of copying items is of course not perfect as we cannot clone every single component, which would be pretty much impossible with our ECS.
+///     We only consider the most important components so the paradox clone gets similar equipment.
+///     This method of using subscriptions was chosen to make it easy for forks to add their own custom components that need to be copied.
+/// </remarks>
+public sealed partial class CloningSystem : EntitySystem
+{
+    [Dependency] private readonly SharedStackSystem _stack = default!;
+    [Dependency] private readonly SharedLabelSystem _label = default!;
+    [Dependency] private readonly ForensicsSystem _forensics = default!;
+    [Dependency] private readonly PaperSystem _paper = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StackComponent, CloningItemEvent>(OnCloneStack);
+        SubscribeLocalEvent<LabelComponent, CloningItemEvent>(OnCloneLabel);
+        SubscribeLocalEvent<PaperComponent, CloningItemEvent>(OnClonePaper);
+        SubscribeLocalEvent<ForensicsComponent, CloningItemEvent>(OnCloneForensics);
+        SubscribeLocalEvent<StoreComponent, CloningItemEvent>(OnCloneStore);
+    }
+
+    private void OnCloneStack(Entity<StackComponent> ent, ref CloningItemEvent args)
+    {
+        // if the clone is a stack as well, adjust the count of the copy
+        if (TryComp<StackComponent>(args.CloneUid, out var cloneStackComp))
+            _stack.SetCount(args.CloneUid, ent.Comp.Count, cloneStackComp);
+    }
+
+    private void OnCloneLabel(Entity<LabelComponent> ent, ref CloningItemEvent args)
+    {
+        // copy the label
+        _label.Label(args.CloneUid, ent.Comp.CurrentLabel);
+    }
+
+    private void OnClonePaper(Entity<PaperComponent> ent, ref CloningItemEvent args)
+    {
+        // copy the text and any stamps
+        if (TryComp<PaperComponent>(args.CloneUid, out var clonePaperComp))
+        {
+            _paper.SetContent((args.CloneUid, clonePaperComp), ent.Comp.Content);
+            _paper.CopyStamps(ent.AsNullable(), (args.CloneUid, clonePaperComp));
+        }
+    }
+
+    private void OnCloneForensics(Entity<ForensicsComponent> ent, ref CloningItemEvent args)
+    {
+        // copy any forensics to the cloned item
+        _forensics.CopyForensicsFrom(ent.Comp, args.CloneUid);
+    }
+
+    private void OnCloneStore(Entity<StoreComponent> ent, ref CloningItemEvent args)
+    {
+        // copy the current amount of currency in the store
+        // at the moment this takes care of uplink implants and the portable nukie uplinks
+        // turning a copied pda into an uplink will need some refactoring first
+        if (TryComp<StoreComponent>(args.CloneUid, out var cloneStoreComp))
+        {
+            cloneStoreComp.Balance = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(ent.Comp.Balance);
+        }
+    }
+
+}
index b8aff813d25e94f444fbbcb341fa88e85eaf8cca..0a0c5d2eff6f542505b8ffb58a4a4da136d76a7e 100644 (file)
@@ -9,7 +9,6 @@ using Content.Shared.Implants;
 using Content.Shared.Implants.Components;
 using Content.Shared.NameModifier.Components;
 using Content.Shared.StatusEffect;
-using Content.Shared.Stacks;
 using Content.Shared.Storage;
 using Content.Shared.Storage.EntitySystems;
 using Content.Shared.Whitelist;
@@ -25,7 +24,7 @@ namespace Content.Server.Cloning;
 ///     System responsible for making a copy of a humanoid's body.
 ///     For the cloning machines themselves look at CloningPodSystem, CloningConsoleSystem and MedicalScannerSystem instead.
 /// </summary>
-public sealed class CloningSystem : EntitySystem
+public sealed partial class CloningSystem : EntitySystem
 {
     [Dependency] private readonly IComponentFactory _componentFactory = default!;
     [Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
@@ -36,7 +35,6 @@ public sealed class CloningSystem : EntitySystem
     [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
     [Dependency] private readonly SharedContainerSystem _container = default!;
     [Dependency] private readonly SharedStorageSystem _storage = default!;
-    [Dependency] private readonly SharedStackSystem _stack = default!;
     [Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
 
     /// <summary>
@@ -157,9 +155,9 @@ public sealed class CloningSystem : EntitySystem
 
         var spawned = EntityManager.SpawnAtPosition(prototype, coords);
 
-        // if the original is a stack, adjust the count of the copy
-        if (TryComp<StackComponent>(original, out var originalStack) && TryComp<StackComponent>(spawned, out var spawnedStack))
-            _stack.SetCount(spawned, originalStack.Count, spawnedStack);
+        // copy over important component data
+        var ev = new CloningItemEvent(spawned);
+        RaiseLocalEvent(original, ref ev);
 
         // if the original has items inside its storage, copy those as well
         if (TryComp<StorageComponent>(original, out var originalStorage) && TryComp<StorageComponent>(spawned, out var spawnedStorage))
@@ -232,7 +230,14 @@ public sealed class CloningSystem : EntitySystem
 
             var targetImplant = _subdermalImplant.AddImplant(target, implantId);
 
-            if (copyStorage && targetImplant != null)
+            if (targetImplant == null)
+                continue;
+
+            // copy over important component data
+            var ev = new CloningItemEvent(targetImplant.Value);
+            RaiseLocalEvent(originalImplant, ref ev);
+
+            if (copyStorage)
                 CopyStorage(originalImplant, targetImplant.Value, whitelist, blacklist); // only needed for storage implants
         }
 
index 27df086ae1b91810bfa4c457d45be337931359d2..66818855e5215808881df9dff4acd5b344e6ae0e 100644 (file)
@@ -138,6 +138,11 @@ namespace Content.Server.Forensics
             {
                 dest.Fingerprints.Add(print);
             }
+
+            foreach (var residue in src.Residues)
+            {
+                dest.Residues.Add(residue);
+            }
         }
 
         public List<string> GetSolutionsDNA(EntityUid uid)
index bd6645404cacb88a4d2a46020f4a8bef2bd35982..7005ff29668c2834dadd711b35a6a762af8dd56d 100644 (file)
@@ -2,12 +2,21 @@ namespace Content.Shared.Cloning.Events;
 
 /// <summary>
 ///    Raised before a mob is cloned. Cancel to prevent cloning.
+///    This is raised on the original mob.
 /// </summary>
 [ByRefEvent]
 public record struct CloningAttemptEvent(CloningSettingsPrototype Settings, bool Cancelled = false);
 
 /// <summary>
-///    Raised after a new mob got spawned when cloning a humanoid.
+///    Raised after a new mob was spawned when cloning a humanoid.
+///    This is raised on the original mob.
 /// </summary>
 [ByRefEvent]
 public record struct CloningEvent(CloningSettingsPrototype Settings, EntityUid CloneUid);
+
+/// <summary>
+///    Raised after a new item was spawned when cloning an item.
+///    This is raised on the original item.
+/// </summary>
+[ByRefEvent]
+public record struct CloningItemEvent(EntityUid CloneUid);
index bbff5ec39bec080d24dfbac4a5d70dead22c8a23..712133c0e6456fb4124c8500217959067512aa6e 100644 (file)
@@ -224,6 +224,26 @@ public sealed class PaperSystem : EntitySystem
         return true;
     }
 
+    /// <summary>
+    ///     Copy any stamp information from one piece of paper to another.
+    /// </summary>
+    public void CopyStamps(Entity<PaperComponent?> source, Entity<PaperComponent?> target)
+    {
+        if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
+            return;
+
+        target.Comp.StampedBy = new List<StampDisplayInfo>(source.Comp.StampedBy);
+        target.Comp.StampState = source.Comp.StampState;
+        Dirty(target);
+
+        if (TryComp<AppearanceComponent>(target, out var appearance))
+        {
+            // delete any stamps if the stamp state is null
+            _appearance.SetData(target, PaperVisuals.Stamp, target.Comp.StampState ?? "", appearance);
+        }
+    }
+
+
     public void SetContent(Entity<PaperComponent> entity, string content)
     {
         entity.Comp.Content = content;
index fa634f4774b3fc9df2e4280cab65e1840c72a801..5a913674e5fc75160ccfb5a2bb6693d678b5ecdd 100644 (file)
@@ -61,6 +61,7 @@
     components:
     - AttachedClothing # helmets, which are part of the suit
     - HumanoidAppearance # will cause problems for downstream felinids getting cloned as Urists
+    - Implanter # they will spawn full again, but you already get the implant. And we can't do item slot copying yet
     - VirtualItem
 
 # all antagonist roles