Split out the CloneComponents into its own method (#37155)
* 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>
This commit is contained in:
@@ -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,30 +61,7 @@ public sealed partial class CloningSystem : EntitySystem
|
||||
clone = coords == null ? Spawn(speciesPrototype.Prototype) : Spawn(speciesPrototype.Prototype, coords.Value);
|
||||
_humanoidSystem.CloneAppearance(original, clone.Value);
|
||||
|
||||
var componentsToCopy = settings.Components;
|
||||
|
||||
// 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)!);
|
||||
|
||||
foreach (var componentName in componentsToCopy)
|
||||
{
|
||||
if (!_componentFactory.TryGetRegistration(componentName, out var componentRegistration))
|
||||
{
|
||||
Log.Error($"Tried to use invalid component registration for cloning: {componentName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
var cloningEv = new CloningEvent(settings, clone.Value);
|
||||
RaiseLocalEvent(original, ref cloningEv); // used for datafields that cannot be directly copied
|
||||
CloneComponents(original, clone.Value, settings);
|
||||
|
||||
// Add equipment first so that SetEntityName also renames the ID card.
|
||||
if (settings.CopyEquipment != null)
|
||||
@@ -98,21 +76,66 @@ public sealed partial class CloningSystem : EntitySystem
|
||||
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;
|
||||
var originalName = _nameMod.GetBaseName(original);
|
||||
|
||||
// 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);
|
||||
// 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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!_componentFactory.TryGetRegistration(componentName, out var componentRegistration))
|
||||
{
|
||||
Log.Error($"Tried to use invalid component registration for cloning: {componentName}");
|
||||
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?
|
||||
{
|
||||
CopyComp(original, clone, sourceComp);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var componentName in componentsToEvent)
|
||||
{
|
||||
if (!_componentFactory.TryGetRegistration(componentName, out var componentRegistration))
|
||||
{
|
||||
Log.Error($"Tried to use invalid component registration for cloning: {componentName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the original does not have the component, then the clone shouldn't have it either.
|
||||
RemComp(clone, componentRegistration.Type);
|
||||
}
|
||||
|
||||
var cloningEv = new CloningEvent(settings, clone);
|
||||
RaiseLocalEvent(original, ref cloningEv); // used for datafields that cannot be directly copied using CopyComp
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the equipment the original has to the clone.
|
||||
/// This uses the original prototype of the items, so any changes to components that are done after spawning are lost!
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user