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);
+ }
+
}
}
}
--- /dev/null
+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);
+ }
+ }
+
+}
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;
/// 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!;
[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>
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))
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
}
{
dest.Fingerprints.Add(print);
}
+
+ foreach (var residue in src.Residues)
+ {
+ dest.Residues.Add(residue);
+ }
}
public List<string> GetSolutionsDNA(EntityUid uid)
/// <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);
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;
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