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.Inventory;
|
||||||
using Content.Shared.Implants;
|
using Content.Shared.Implants;
|
||||||
using Content.Shared.Implants.Components;
|
using Content.Shared.Implants.Components;
|
||||||
using Content.Shared.NameModifier.Components;
|
using Content.Shared.NameModifier.EntitySystems;
|
||||||
using Content.Shared.StatusEffect;
|
using Content.Shared.StatusEffect;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Content.Shared.Storage.EntitySystems;
|
using Content.Shared.Storage.EntitySystems;
|
||||||
@@ -36,6 +36,7 @@ public sealed partial class CloningSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
||||||
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
|
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
|
||||||
|
[Dependency] private readonly NameModifierSystem _nameMod = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Spawns a clone of the given humanoid mob at the specified location or in nullspace.
|
/// 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);
|
clone = coords == null ? Spawn(speciesPrototype.Prototype) : Spawn(speciesPrototype.Prototype, coords.Value);
|
||||||
_humanoidSystem.CloneAppearance(original, clone.Value);
|
_humanoidSystem.CloneAppearance(original, clone.Value);
|
||||||
|
|
||||||
var componentsToCopy = settings.Components;
|
CloneComponents(original, clone.Value, settings);
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Add equipment first so that SetEntityName also renames the ID card.
|
// Add equipment first so that SetEntityName also renames the ID card.
|
||||||
if (settings.CopyEquipment != null)
|
if (settings.CopyEquipment != null)
|
||||||
@@ -98,21 +76,66 @@ public sealed partial class CloningSystem : EntitySystem
|
|||||||
if (settings.CopyImplants)
|
if (settings.CopyImplants)
|
||||||
CopyImplants(original, clone.Value, settings.CopyInternalStorage, settings.Whitelist, settings.Blacklist);
|
CopyImplants(original, clone.Value, settings.CopyInternalStorage, settings.Whitelist, settings.Blacklist);
|
||||||
|
|
||||||
var originalName = Name(original);
|
var originalName = _nameMod.GetBaseName(original);
|
||||||
if (TryComp<NameModifierComponent>(original, out var nameModComp)) // if the originals name was modified, use the unmodified name
|
|
||||||
originalName = nameModComp.BaseName;
|
|
||||||
|
|
||||||
// This will properly set the BaseName and EntityName for the clone.
|
// Set the clone's name. The raised events will also adjust their PDA and ID card names.
|
||||||
// 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);
|
_metaData.SetEntityName(clone.Value, originalName);
|
||||||
|
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}");
|
||||||
return true;
|
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>
|
/// <summary>
|
||||||
/// Copies the equipment the original has to the clone.
|
/// 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!
|
/// 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.Inventory;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Prototypes;
|
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.Custom.Prototype.Array;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||||
|
|
||||||
namespace Content.Shared.Cloning;
|
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
|
/// TODO: Make this not a string https://github.com/space-wizards/RobustToolbox/issues/5709
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Components to copy from the original to the clone.
|
/// Components to copy from the original to the clone using CopyComp.
|
||||||
/// This only makes a shallow copy of datafields!
|
/// This makes a deepcopy of all datafields, including information the clone might not own!
|
||||||
/// If you need a deep copy or additional component initialization, then subscribe to CloningEvent instead!
|
/// 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>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
[AlwaysPushInheritance]
|
[AlwaysPushInheritance]
|
||||||
public HashSet<string> Components = new();
|
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