diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs
index 9c87b80a2a..959b3987a9 100644
--- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs
+++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs
@@ -14,7 +14,8 @@ namespace Content.Shared.Xenoarchaeology.Artifact.Components;
/// This is used for handling interactions with artifacts as well as
/// storing data about artifact node graphs.
///
-[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState, AutoGenerateComponentPause]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
+[Access(typeof(SharedXenoArtifactSystem))]
public sealed partial class XenoArtifactComponent : Component
{
public static string NodeContainerId = "node-container";
@@ -192,6 +193,15 @@ public sealed partial class XenoArtifactComponent : Component
///
[DataField]
public LocId? UnlockFailureMsg = "artifact-unlock-state-end-failure";
+
+ ///
+ /// List of currently attached entities - node scanners, etc.
+ ///
+ ///
+ /// TODO: replace with relationship system.
+ ///
+ [DataField, AutoNetworkedField]
+ public HashSet AttachedEntities = new();
}
///
diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs
index 2cd02a1d13..deca5e4dee 100644
--- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs
+++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs
@@ -40,6 +40,17 @@ public abstract partial class SharedXenoArtifactSystem : EntitySystem
UpdateUnlock(frameTime);
}
+ /// Clears all attached scanners.
+ [SubscribeLocalEvent]
+ private void OnShutdown(Entity ent, ref ComponentRemove shutdown)
+ {
+ var removedEvent = new XenoArtifactDestroyedEvent();
+ foreach (var entity in ent.Comp.AttachedEntities)
+ {
+ RaiseLocalEvent(entity, ref removedEvent);
+ }
+ }
+
/// As all artifacts have to contain nodes - we ensure that they are containers.
private void OnStartup(Entity ent, ref ComponentStartup args)
{
@@ -52,6 +63,51 @@ public abstract partial class SharedXenoArtifactSystem : EntitySystem
args.Handled = TryActivateXenoArtifact(ent, ent, null, Transform(ent).Coordinates, false);
}
+ ///
+ /// Tries to remove an entity from the list of attached entities.
+ /// This helps with tracking relationship.
+ ///
+ /// Artifact entity.
+ /// Entity to detach.
+ ///
+ /// Returns false if is not artifact,
+ /// or if there is no such entity in list of attached ones.
+ /// Otherwise, returns true.
+ ///
+ public bool TryDetachEntity(Entity ent, EntityUid entityToDetach)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return false;
+
+ var result = ent.Comp.AttachedEntities.Remove(entityToDetach);
+ if (result)
+ Dirty(ent);
+
+ return result;
+ }
+
+ ///
+ /// Tries to add an entity to list of attached entities.
+ /// This helps with tracking relationship.
+ ///
+ /// Artifact entity.
+ /// Entity to attach.
+ ///
+ /// Returns False if is not artifact,
+ /// or entity is already added, otherwise true.
+ ///
+ public bool TryAttachEntity(Entity ent, EntityUid entityToAttach)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return false;
+
+ var result = ent.Comp.AttachedEntities.Add(entityToAttach);
+ if (result)
+ Dirty(ent);
+
+ return result;
+ }
+
public void SetSuppressed(Entity ent, bool val)
{
if (ent.Comp.Suppressed == val)
@@ -61,3 +117,7 @@ public abstract partial class SharedXenoArtifactSystem : EntitySystem
Dirty(ent);
}
}
+
+/// Event of artifact destruction.
+[ByRefEvent]
+public record struct XenoArtifactDestroyedEvent;
diff --git a/Content.Shared/Xenoarchaeology/Equipment/Components/NodeScannerComponent.cs b/Content.Shared/Xenoarchaeology/Equipment/Components/NodeScannerComponent.cs
index 72223a366b..1b29ddeab0 100644
--- a/Content.Shared/Xenoarchaeology/Equipment/Components/NodeScannerComponent.cs
+++ b/Content.Shared/Xenoarchaeology/Equipment/Components/NodeScannerComponent.cs
@@ -28,6 +28,7 @@ public sealed partial class NodeScannerComponent : Component
/// Component-marker that node scanner device () is connected to artifact.
///
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause]
+[Access(typeof(NodeScannerSystem))]
public sealed partial class NodeScannerConnectedComponent : Component
{
///
diff --git a/Content.Shared/Xenoarchaeology/Equipment/NodeScannerSystem.cs b/Content.Shared/Xenoarchaeology/Equipment/NodeScannerSystem.cs
index 60c027736c..78301b0376 100644
--- a/Content.Shared/Xenoarchaeology/Equipment/NodeScannerSystem.cs
+++ b/Content.Shared/Xenoarchaeology/Equipment/NodeScannerSystem.cs
@@ -1,6 +1,7 @@
using Content.Shared.Interaction;
using Content.Shared.Timing.Systems;
using Content.Shared.Verbs;
+using Content.Shared.Xenoarchaeology.Artifact;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Content.Shared.Xenoarchaeology.Equipment.Components;
using Robust.Shared.Timing;
@@ -14,13 +15,7 @@ public sealed partial class NodeScannerSystem : EntitySystem
[Dependency] private IGameTiming _timing = default!;
[Dependency] private SharedUserInterfaceSystem _ui = default!;
[Dependency] private SharedTransformSystem _transform = default!;
-
- ///
- public override void Initialize()
- {
- SubscribeLocalEvent(OnBeforeRangedInteract);
- SubscribeLocalEvent>(AddScanVerb);
- }
+ [Dependency] private SharedXenoArtifactSystem _artifact = default!;
///
public override void Update(float frameTime)
@@ -43,7 +38,27 @@ public sealed partial class NodeScannerSystem : EntitySystem
}
}
- private void OnBeforeRangedInteract(EntityUid uid, NodeScannerComponent component, BeforeRangedInteractEvent args)
+ /// Disconnect if artifact is destroyed
+ [SubscribeLocalEvent]
+ private void OnArtifactRemoved(Entity ent, ref XenoArtifactDestroyedEvent args)
+ {
+ RemCompDeferred(ent, ent.Comp);
+ }
+
+ /// Detach if scanner is disconnected, or destroyed.
+ [SubscribeLocalEvent]
+ private void OnScannerRemoved(Entity ent, ref ComponentRemove args)
+ {
+ var artifact = ent.Comp.AttachedTo;
+ if (!TerminatingOrDeleted(artifact))
+ {
+ _artifact.TryDetachEntity((artifact, null), ent);
+ }
+ }
+
+ /// Attach scanner if target is fitting.
+ [SubscribeLocalEvent]
+ private void OnBeforeRangedInteract(Entity ent, ref BeforeRangedInteractEvent args)
{
if (args.Handled || !args.CanReach || args.Target is not { } target || !HasComp(target))
return;
@@ -52,22 +67,26 @@ public sealed partial class NodeScannerSystem : EntitySystem
? (target, unlockingComponent)
: (target, null);
- Attach((uid, component), unlockingEnt, args.User);
+ Attach(ent, unlockingEnt, args.User);
args.Handled = true;
}
- private void AddScanVerb(EntityUid uid, NodeScannerComponent component, GetVerbsEvent args)
+ /// Add `scan` verb if target is fitting.
+ [SubscribeLocalEvent]
+ private void AddScanVerb(Entity ent, ref GetVerbsEvent args)
{
if (!args.CanAccess)
return;
- if (!TryComp(args.Target, out var unlockingComponent))
+ var target = args.Target;
+ if (!TryComp(target, out var unlockingComponent))
return;
+ var user = args.User;
var verb = new UtilityVerb
{
- Act = () => Attach((uid, component), (args.Target, unlockingComponent), args.User),
+ Act = () => Attach(ent, (target, unlockingComponent), user),
Text = Loc.GetString("node-scan-tooltip")
};
@@ -84,11 +103,17 @@ public sealed partial class NodeScannerSystem : EntitySystem
return;
var connected = EnsureComp(device);
+
EntityUid artifact = unlockingEnt;
if (connected.AttachedTo != artifact)
{
+ // Remove connection from previous scanner.
+ if (connected.AttachedTo.Valid)
+ _artifact.TryDetachEntity(connected.AttachedTo, device);
+
connected.AttachedTo = artifact;
Dirty(device, connected);
+ _artifact.TryAttachEntity(artifact, device);
}
_ui.TryOpenUi((device, null), NodeScannerUiKey.Key, actor, predicted: true);