]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Split out the CloneComponents into its own method (#37155)
authorpoklj <compgeek223@gmail.com>
Tue, 6 May 2025 17:22:32 +0000 (14:22 -0300)
committerGitHub <noreply@github.com>
Tue, 6 May 2025 17:22:32 +0000 (13:22 -0400)
* Split out the CloneComponents into its own method

* CR - Move some extra info in

- add TryComp for status effects

- Move some remcomps around

- Make Special event raising components to handle special
components that reference entities that have ownership

* CR - Extra recommendation on the prototype

thanks slarti

* Solve the yaml linter problem

* CR - Typos, grammar and some extra Status effect

* cleanup

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Content.Server/Cloning/CloningSystem.cs
Content.Shared/Cloning/CloningSettingsPrototype.cs

index 0a0c5d2eff6f542505b8ffb58a4a4da136d76a7e..adf7acb7bd3ea33b35a61a9a9a00e43ddd26b857 100644 (file)
@@ -7,7 +7,7 @@ using Content.Shared.Humanoid;
 using Content.Shared.Inventory;
 using Content.Shared.Implants;
 using Content.Shared.Implants.Components;
-using Content.Shared.NameModifier.Components;
+using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.StatusEffect;
 using Content.Shared.Storage;
 using Content.Shared.Storage.EntitySystems;
@@ -36,6 +36,7 @@ public sealed partial class CloningSystem : EntitySystem
     [Dependency] private readonly SharedContainerSystem _container = default!;
     [Dependency] private readonly SharedStorageSystem _storage = default!;
     [Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
     /// <summary>
     ///     Spawns a clone of the given humanoid mob at the specified location or in nullspace.
@@ -60,11 +61,48 @@ public sealed partial class CloningSystem : EntitySystem
         clone = coords == null ? Spawn(speciesPrototype.Prototype) : Spawn(speciesPrototype.Prototype, coords.Value);
         _humanoidSystem.CloneAppearance(original, clone.Value);
 
+        CloneComponents(original, clone.Value, settings);
+
+        // Add equipment first so that SetEntityName also renames the ID card.
+        if (settings.CopyEquipment != null)
+            CopyEquipment(original, clone.Value, settings.CopyEquipment.Value, settings.Whitelist, settings.Blacklist);
+
+        // Copy storage on the mob itself as well.
+        // This is needed for slime storage.
+        if (settings.CopyInternalStorage)
+            CopyStorage(original, clone.Value, settings.Whitelist, settings.Blacklist);
+
+        // copy implants and their storage contents
+        if (settings.CopyImplants)
+            CopyImplants(original, clone.Value, settings.CopyInternalStorage, settings.Whitelist, settings.Blacklist);
+
+        var originalName = _nameMod.GetBaseName(original);
+
+        // Set the clone's name. The raised events will also adjust their PDA and ID card names.
+        _metaData.SetEntityName(clone.Value, originalName);
+
+        _adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}");
+        return true;
+    }
+
+    /// <summary>
+    ///     Copy components from one entity to another based on a CloningSettingsPrototype.
+    /// </summary>
+    /// <param name="original">The orignal Entity to clone components from.</param>
+    /// <param name="clone">The target Entity to clone components to.</param>
+    /// <param name="settings">The clone settings prototype containing the list of components to clone.</param>
+    public void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings)
+    {
         var componentsToCopy = settings.Components;
+        var componentsToEvent = settings.EventComponents;
 
         // don't make status effects permanent
         if (TryComp<StatusEffectsComponent>(original, out var statusComp))
-            componentsToCopy.ExceptWith(statusComp.ActiveEffects.Values.Select(s => s.RelevantComponent).Where(s => s != null)!);
+        {
+            var statusComps = statusComp.ActiveEffects.Values.Select(s => s.RelevantComponent).Where(s => s != null).ToList();
+            componentsToCopy.ExceptWith(statusComps!);
+            componentsToEvent.ExceptWith(statusComps!);
+        }
 
         foreach (var componentName in componentsToCopy)
         {
@@ -74,43 +112,28 @@ public sealed partial class CloningSystem : EntitySystem
                 continue;
             }
 
+            // If the original does not have the component, then the clone shouldn't have it either.
+            RemComp(clone, componentRegistration.Type);
             if (EntityManager.TryGetComponent(original, componentRegistration.Type, out var sourceComp)) // Does the original have this component?
             {
-                if (HasComp(clone.Value, componentRegistration.Type)) // CopyComp cannot overwrite existing components
-                    RemComp(clone.Value, componentRegistration.Type);
-                CopyComp(original, clone.Value, sourceComp);
+                CopyComp(original, clone, sourceComp);
             }
         }
 
-        var cloningEv = new CloningEvent(settings, clone.Value);
-        RaiseLocalEvent(original, ref cloningEv); // used for datafields that cannot be directly copied
-
-        // Add equipment first so that SetEntityName also renames the ID card.
-        if (settings.CopyEquipment != null)
-            CopyEquipment(original, clone.Value, settings.CopyEquipment.Value, settings.Whitelist, settings.Blacklist);
-
-        // Copy storage on the mob itself as well.
-        // This is needed for slime storage.
-        if (settings.CopyInternalStorage)
-            CopyStorage(original, clone.Value, settings.Whitelist, settings.Blacklist);
-
-        // copy implants and their storage contents
-        if (settings.CopyImplants)
-            CopyImplants(original, clone.Value, settings.CopyInternalStorage, settings.Whitelist, settings.Blacklist);
-
-        var originalName = Name(original);
-        if (TryComp<NameModifierComponent>(original, out var nameModComp)) // if the originals name was modified, use the unmodified name
-            originalName = nameModComp.BaseName;
+        foreach (var componentName in componentsToEvent)
+        {
+            if (!_componentFactory.TryGetRegistration(componentName, out var componentRegistration))
+            {
+                Log.Error($"Tried to use invalid component registration for cloning: {componentName}");
+                continue;
+            }
 
-        // This will properly set the BaseName and EntityName for the clone.
-        // Adding the component first before renaming will make sure RefreshNameModifers is called.
-        // Without this the name would get reverted to Urist.
-        // If the clone has no name modifiers, NameModifierComponent will be removed again.
-        EnsureComp<NameModifierComponent>(clone.Value);
-        _metaData.SetEntityName(clone.Value, originalName);
+            // If the original does not have the component, then the clone shouldn't have it either.
+            RemComp(clone, componentRegistration.Type);
+        }
 
-        _adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}");
-        return true;
+        var cloningEv = new CloningEvent(settings, clone);
+        RaiseLocalEvent(original, ref cloningEv); // used for datafields that cannot be directly copied using CopyComp
     }
 
     /// <summary>
index b5cfc0500dc282f3813f5c9b780d942a38d819f0..8cd22f116795717d2a2ff1d76faf4be16f855827 100644 (file)
@@ -1,7 +1,9 @@
 using Content.Shared.Inventory;
 using Content.Shared.Whitelist;
 using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
 
 namespace Content.Shared.Cloning;
 
@@ -62,11 +64,20 @@ public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPr
 
     /// TODO: Make this not a string https://github.com/space-wizards/RobustToolbox/issues/5709
     /// <summary>
-    ///     Components to copy from the original to the clone.
-    ///     This only makes a shallow copy of datafields!
-    ///     If you need a deep copy or additional component initialization, then subscribe to CloningEvent instead!
+    ///     Components to copy from the original to the clone using CopyComp.
+    ///     This makes a deepcopy of all datafields, including information the clone might not own!
+    ///     If you need to exclude data or do additional component initialization, then subscribe to CloningEvent instead!
+    ///     Components in this list that the orginal does not have will be removed from the clone.
     /// </summary>
     [DataField]
     [AlwaysPushInheritance]
     public HashSet<string> Components = new();
+
+    /// <summary>
+    ///     Components to remove from the clone and copy over manually using a CloneEvent raised on the original.
+    ///     Use this when the component cannot be copied using CopyComp, for example when having an Uid as a datafield.
+    ///</summary>
+    [DataField]
+    [AlwaysPushInheritance]
+    public HashSet<string> EventComponents = new();
 }