diff --git a/Content.Server/MachineLinking/Components/AutoLinkReceiverComponent.cs b/Content.Server/MachineLinking/Components/AutoLinkReceiverComponent.cs
new file mode 100644
index 0000000000..7bb9f99a4e
--- /dev/null
+++ b/Content.Server/MachineLinking/Components/AutoLinkReceiverComponent.cs
@@ -0,0 +1,12 @@
+namespace Content.Server.MachineLinking.Components;
+
+///
+/// This is used for automatic linkage with buttons and other transmitters.
+///
+[RegisterComponent]
+public sealed class AutoLinkReceiverComponent : Component
+{
+ [DataField("channel", required: true, readOnly: true)]
+ public string AutoLinkChannel = default!;
+}
+
diff --git a/Content.Server/MachineLinking/Components/AutoLinkTransmitterComponent.cs b/Content.Server/MachineLinking/Components/AutoLinkTransmitterComponent.cs
new file mode 100644
index 0000000000..b3760a2f2c
--- /dev/null
+++ b/Content.Server/MachineLinking/Components/AutoLinkTransmitterComponent.cs
@@ -0,0 +1,12 @@
+namespace Content.Server.MachineLinking.Components;
+
+///
+/// This is used for automatic linkage with various receivers, like shutters.
+///
+[RegisterComponent]
+public sealed class AutoLinkTransmitterComponent : Component
+{
+ [DataField("channel", required: true, readOnly: true)]
+ public string AutoLinkChannel = default!;
+}
+
diff --git a/Content.Server/MachineLinking/System/AutoLinkSystem.cs b/Content.Server/MachineLinking/System/AutoLinkSystem.cs
new file mode 100644
index 0000000000..c7590e564d
--- /dev/null
+++ b/Content.Server/MachineLinking/System/AutoLinkSystem.cs
@@ -0,0 +1,36 @@
+using Content.Server.MachineLinking.Components;
+
+namespace Content.Server.MachineLinking.System;
+
+///
+/// This handles automatically linking autolinked entities at round-start.
+///
+public sealed class AutoLinkSystem : EntitySystem
+{
+ [Dependency] private readonly SignalLinkerSystem _signalLinkerSystem = default!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnAutoLinkMapInit);
+ }
+
+ private void OnAutoLinkMapInit(EntityUid uid, AutoLinkTransmitterComponent component, MapInitEvent args)
+ {
+ var xform = Transform(uid);
+
+ foreach (var receiver in EntityQuery())
+ {
+ if (receiver.AutoLinkChannel != component.AutoLinkChannel)
+ continue; // Not ours.
+
+ var rxXform = Transform(receiver.Owner);
+
+ if (rxXform.GridUid != xform.GridUid)
+ continue;
+
+ _signalLinkerSystem.TryLinkDefaults(receiver.Owner, uid, null);
+ }
+ }
+}
+
diff --git a/Content.Server/MachineLinking/System/SignalLinkerSystem.cs b/Content.Server/MachineLinking/System/SignalLinkerSystem.cs
index 4011d5e7ab..edc7ab9c78 100644
--- a/Content.Server/MachineLinking/System/SignalLinkerSystem.cs
+++ b/Content.Server/MachineLinking/System/SignalLinkerSystem.cs
@@ -277,12 +277,15 @@ namespace Content.Server.MachineLinking.System
}
- private bool TryLink(SignalTransmitterComponent transmitter, SignalReceiverComponent receiver, SignalPortSelected args, EntityUid user, bool quiet = false, bool checkRange = true)
+ private bool TryLink(SignalTransmitterComponent transmitter, SignalReceiverComponent receiver, SignalPortSelected args, EntityUid? user, bool quiet = false, bool checkRange = true)
{
if (!transmitter.Outputs.TryGetValue(args.TransmitterPort, out var linkedReceivers) ||
!receiver.Inputs.TryGetValue(args.ReceiverPort, out var linkedTransmitters))
return false;
+ // Accounts for possibly missing user & the quiet option.
+ var alertFilter = (!quiet && user != null) ? Filter.Entities(user.Value) : null;
+
// Does the link already exist? Under the assumption that nothing has broken, lets only check the
// transmitter ports.
foreach (var identifier in linkedTransmitters)
@@ -293,9 +296,9 @@ namespace Content.Server.MachineLinking.System
if (checkRange && !IsInRange(transmitter, receiver))
{
- if (!quiet)
+ if (alertFilter != null)
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-out-of-range"),
- Filter.Entities(user));
+ alertFilter);
return false;
}
@@ -304,27 +307,27 @@ namespace Content.Server.MachineLinking.System
RaiseLocalEvent(transmitter.Owner, linkAttempt, true);
if (linkAttempt.Cancelled)
{
- if (!quiet)
+ if (alertFilter != null)
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-connection-refused", ("machine", transmitter.Owner)),
- Filter.Entities(user));
+ alertFilter);
return false;
}
RaiseLocalEvent(receiver.Owner, linkAttempt, true);
if (linkAttempt.Cancelled)
{
- if (!quiet)
+ if (alertFilter != null)
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-connection-refused", ("machine", receiver.Owner)),
- Filter.Entities(user));
+ alertFilter);
return false;
}
linkedReceivers.Add(new(receiver.Owner, args.ReceiverPort));
linkedTransmitters.Add(new(transmitter.Owner, args.TransmitterPort));
- if (!quiet)
+ if (alertFilter != null)
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-linked-port",
("machine1", transmitter.Owner), ("port1", PortName(args.TransmitterPort)),
("machine2", receiver.Owner), ("port2", PortName(args.ReceiverPort))),
- Filter.Entities(user), PopupType.Medium);
+ alertFilter, PopupType.Medium);
var newLink = new NewLinkEvent(user, transmitter.Owner, args.TransmitterPort, receiver.Owner, args.ReceiverPort);
RaiseLocalEvent(receiver.Owner, newLink);
@@ -417,7 +420,7 @@ namespace Content.Server.MachineLinking.System
/// Attempt to link all default ports connections. Returns true if all links succeeded. Otherwise returns
/// false.
///
- public bool TryLinkDefaults(EntityUid receiverUid, EntityUid transmitterUid, EntityUid user,
+ public bool TryLinkDefaults(EntityUid receiverUid, EntityUid transmitterUid, EntityUid? user,
SignalReceiverComponent? receiver = null, SignalTransmitterComponent? transmitter = null)
{
if (!Resolve(receiverUid, ref receiver, false) || !Resolve(transmitterUid, ref transmitter, false))
diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs
index ea66232c65..692b574f0f 100644
--- a/Content.Shared/Access/Systems/AccessReaderSystem.cs
+++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs
@@ -26,7 +26,9 @@ namespace Content.Shared.Access.Systems
private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkAttemptEvent args)
{
- if (component.Enabled && !IsAllowed(args.User, component))
+ if (args.User == null) // AutoLink (and presumably future external linkers) have no user.
+ return;
+ if (component.Enabled && !IsAllowed(args.User.Value, component))
args.Cancel();
}
diff --git a/Content.Shared/MachineLinking/Events/LinkAttemptEvent.cs b/Content.Shared/MachineLinking/Events/LinkAttemptEvent.cs
index 8def85fbd6..4eb75bdc7c 100644
--- a/Content.Shared/MachineLinking/Events/LinkAttemptEvent.cs
+++ b/Content.Shared/MachineLinking/Events/LinkAttemptEvent.cs
@@ -4,11 +4,11 @@ namespace Content.Shared.MachineLinking.Events
{
public readonly EntityUid Transmitter;
public readonly EntityUid Receiver;
- public readonly EntityUid User;
+ public readonly EntityUid? User;
public readonly string TransmitterPort;
public readonly string ReceiverPort;
- public LinkAttemptEvent(EntityUid user, EntityUid transmitter, string transmitterPort, EntityUid receiver, string receiverPort)
+ public LinkAttemptEvent(EntityUid? user, EntityUid transmitter, string transmitterPort, EntityUid receiver, string receiverPort)
{
User = user;
Transmitter = transmitter;
diff --git a/Content.Shared/MachineLinking/Events/NewLinkEvent.cs b/Content.Shared/MachineLinking/Events/NewLinkEvent.cs
index f4ee0a1d79..22dfce7b8f 100644
--- a/Content.Shared/MachineLinking/Events/NewLinkEvent.cs
+++ b/Content.Shared/MachineLinking/Events/NewLinkEvent.cs
@@ -4,11 +4,11 @@ namespace Content.Shared.MachineLinking.Events
{
public readonly EntityUid Transmitter;
public readonly EntityUid Receiver;
- public readonly EntityUid User;
+ public readonly EntityUid? User;
public readonly string TransmitterPort;
public readonly string ReceiverPort;
- public NewLinkEvent(EntityUid user, EntityUid transmitter, string transmitterPort, EntityUid receiver, string receiverPort)
+ public NewLinkEvent(EntityUid? user, EntityUid transmitter, string transmitterPort, EntityUid receiver, string receiverPort)
{
User = user;
Transmitter = transmitter;
diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml
new file mode 100644
index 0000000000..399760616c
--- /dev/null
+++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/blast_door_autolink.yml
@@ -0,0 +1,85 @@
+# AutoLink variants of BlastDoor (from Outer Rim)
+
+- type: entity
+ id: BlastDoorExterior1
+ parent: BlastDoor
+ suffix: Autolink, Ext1
+ components:
+ - type: AutoLinkReceiver
+ channel: Ext1
+
+
+- type: entity
+ id: BlastDoorExterior1Open
+ parent: BlastDoorOpen
+ suffix: Open, Autolink, Ext1
+ components:
+ - type: AutoLinkReceiver
+ channel: Ext1
+
+- type: entity
+ id: BlastDoorExterior2
+ parent: BlastDoor
+ suffix: Autolink, Ext2
+ components:
+ - type: AutoLinkReceiver
+ channel: Ext2
+
+
+- type: entity
+ id: BlastDoorExterior2Open
+ parent: BlastDoorOpen
+ suffix: Open, Autolink, Ext2
+ components:
+ - type: AutoLinkReceiver
+ channel: Ext2
+
+- type: entity
+ id: BlastDoorExterior3
+ parent: BlastDoor
+ suffix: Autolink, Ext3
+ components:
+ - type: AutoLinkReceiver
+ channel: Ext3
+
+
+- type: entity
+ id: BlastDoorExterior3Open
+ parent: BlastDoorOpen
+ suffix: Open, Autolink, Ext3
+ components:
+ - type: AutoLinkReceiver
+ channel: Ext3
+
+- type: entity
+ id: BlastDoorBridge
+ parent: BlastDoor
+ suffix: Autolink, Bridge
+ components:
+ - type: AutoLinkReceiver
+ channel: Bridge
+
+- type: entity
+ id: BlastDoorBridgeOpen
+ parent: BlastDoorOpen
+ suffix: Open, Autolink, Bridge
+ components:
+ - type: AutoLinkReceiver
+ channel: Bridge
+
+- type: entity
+ id: BlastDoorWindows
+ parent: BlastDoor
+ suffix: Autolink, Windows
+ components:
+ - type: AutoLinkReceiver
+ channel: Windows
+
+- type: entity
+ id: BlastDoorWindowsOpen
+ parent: BlastDoorOpen
+ suffix: Open, Autolink, Windows
+ components:
+ - type: AutoLinkReceiver
+ channel: Windows
+
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/switch_autolink.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/switch_autolink.yml
new file mode 100644
index 0000000000..fc15956225
--- /dev/null
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/switch_autolink.yml
@@ -0,0 +1,47 @@
+# AutoLink variants of SignalButton (from Outer Rim)
+
+- type: entity
+ id: SignalButtonExt1
+ parent: SignalButton
+ name: exterior button 1
+ suffix: Autolink, Ext1
+ components:
+ - type: AutoLinkTransmitter
+ channel: Ext1
+
+- type: entity
+ id: SignalButtonExt2
+ parent: SignalButton
+ name: exterior button 2
+ suffix: Autolink, Ext2
+ components:
+ - type: AutoLinkTransmitter
+ channel: Ext2
+
+- type: entity
+ id: SignalButtonExt3
+ parent: SignalButton
+ name: exterior button 3
+ suffix: Autolink, Ext3
+ components:
+ - type: AutoLinkTransmitter
+ channel: Ext3
+
+- type: entity
+ id: SignalButtonBridge
+ parent: SignalButton
+ name: bridge windows button
+ suffix: Autolink, Bridge
+ components:
+ - type: AutoLinkTransmitter
+ channel: Bridge
+
+- type: entity
+ id: SignalButtonWindows
+ parent: SignalButton
+ name: exterior windows button
+ suffix: Autolink, Windows
+ components:
+ - type: AutoLinkTransmitter
+ channel: Windows
+