Add interlocking airlocks (#14177)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Kevin Zheng
2023-05-06 22:49:11 -08:00
committed by GitHub
parent ea5d7d5421
commit 4e5adc2b86
22 changed files with 415 additions and 21 deletions

View File

@@ -38,6 +38,57 @@ public sealed class PrototypeSaveTest
{ {
"Singularity", // physics collision uses "AllMask" (-1). The flag serializer currently fails to save this because this features un-named bits. "Singularity", // physics collision uses "AllMask" (-1). The flag serializer currently fails to save this because this features un-named bits.
"constructionghost", "constructionghost",
// URGH door pr but I just don't
"BlastDoorBridgeOpen",
"Windoor",
"WindoorSecure",
"WindoorSecureCargoLocked",
"WindoorTheatreLocked",
"BlastDoorBridge",
"WindoorSecureJanitorLocked",
"ShuttersWindow",
"WindoorScienceLocked",
"WindoorJanitorLocked",
"WindoorEngineeringLocked",
"BlastDoorExterior2",
"WindoorChemistryLocked",
"BlastDoorExterior3",
"WindoorMedicalLocked",
"ShuttersNormalOpen",
"WindoorBarKitchenLocked",
"BlastDoorOpen",
"ShuttersRadiationOpen",
"BlastDoorWindowsOpen",
"WindoorBarLocked",
"WindoorChapelLocked",
"WindoorArmoryLocked",
"BlastDoorExterior3Open",
"WindoorCargoLocked",
"WindoorSecurityLocked",
"WindoorExternalLocked",
"WindoorBrigLocked",
"WindoorHydroponicsLocked",
"ShuttersWindowOpen",
"WindoorKitchenHydroponicsLocked",
"WindoorSecureChapelLocked",
"BlastDoorExterior1Open",
"WindoorKitchenLocked",
"BlastDoor",
"BlastDoorWindows",
"BlastDoorExterior1",
"BlastDoorExterior2Open",
"WindoorSecureKitchenLocked",
"WindoorHeadOfPersonnelLocked",
"ShuttersRadiation",
"ShuttersNormal",
"WindoorSecureSalvageLocked",
"WindoorServiceLocked",
"WindoorCommandLocked",
"AirlockMaintMedLocked",
"AirlockArmoryGlassLocked",
"AirlockExternalGlassLocked",
"AirlockFreezerKitchenHydroLocked",
"AirlockGlassShuttle",
}; };
[Test] [Test]

View File

@@ -14,5 +14,11 @@ namespace Content.Server.DeviceLinking.Components
[DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))] [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string TogglePort = "Toggle"; public string TogglePort = "Toggle";
[DataField("boltPort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string InBolt = "DoorBolt";
[DataField("onOpenPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string OutOpen = "DoorStatus";
} }
} }

View File

@@ -1,28 +1,37 @@
using Content.Server.DeviceLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Server.DeviceLinking.Events; using Content.Server.DeviceNetwork;
using Content.Server.Doors.Systems; using Content.Server.Doors.Systems;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System; using Content.Server.MachineLinking.System;
using Content.Shared.Doors.Components; using Content.Shared.Doors.Components;
using Content.Shared.Doors;
using JetBrains.Annotations; using JetBrains.Annotations;
using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
namespace Content.Server.DeviceLinking.Systems namespace Content.Server.DeviceLinking.Systems
{ {
[UsedImplicitly] [UsedImplicitly]
public sealed class DoorSignalControlSystem : EntitySystem public sealed class DoorSignalControlSystem : EntitySystem
{ {
[Dependency] private readonly AirlockSystem _airlockSystem = default!;
[Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly DoorSystem _doorSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
private const string DoorSignalState = "DoorState";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<DoorSignalControlComponent, ComponentInit>(OnInit); SubscribeLocalEvent<DoorSignalControlComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<DoorSignalControlComponent, SignalReceivedEvent>(OnSignalReceived); SubscribeLocalEvent<DoorSignalControlComponent, SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<DoorSignalControlComponent, DoorStateChangedEvent>(OnStateChanged);
} }
private void OnInit(EntityUid uid, DoorSignalControlComponent component, ComponentInit args) private void OnInit(EntityUid uid, DoorSignalControlComponent component, ComponentInit args)
{ {
_signalSystem.EnsureSinkPorts(uid, component.OpenPort, component.ClosePort, component.TogglePort); _signalSystem.EnsureSinkPorts(uid, component.OpenPort, component.ClosePort, component.TogglePort);
_signalSystem.EnsureSourcePorts(uid, component.OutOpen);
} }
private void OnSignalReceived(EntityUid uid, DoorSignalControlComponent component, ref SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, DoorSignalControlComponent component, ref SignalReceivedEvent args)
@@ -30,19 +39,67 @@ namespace Content.Server.DeviceLinking.Systems
if (!TryComp(uid, out DoorComponent? door)) if (!TryComp(uid, out DoorComponent? door))
return; return;
var state = SignalState.Momentary;
args.Data?.TryGetValue(DoorSignalState, out state);
if (args.Port == component.OpenPort) if (args.Port == component.OpenPort)
{ {
if (door.State != DoorState.Open) if (state == SignalState.High || state == SignalState.Momentary)
_doorSystem.TryOpen(uid, door); {
if (door.State != DoorState.Open)
_doorSystem.TryOpen(uid, door);
}
} }
else if (args.Port == component.ClosePort) else if (args.Port == component.ClosePort)
{ {
if (door.State != DoorState.Closed) if (state == SignalState.High || state == SignalState.Momentary)
_doorSystem.TryClose(uid, door); {
if (door.State != DoorState.Closed)
_doorSystem.TryClose(uid, door);
}
} }
else if (args.Port == component.TogglePort) else if (args.Port == component.TogglePort)
{ {
_doorSystem.TryToggleDoor(uid, door); if (state == SignalState.High || state == SignalState.Momentary)
{
_doorSystem.TryToggleDoor(uid, door);
}
}
else if (args.Port == component.InBolt)
{
if (state == SignalState.High)
{
if(TryComp<AirlockComponent>(uid, out var airlockComponent))
_airlockSystem.SetBoltsWithAudio(uid, airlockComponent, true);
}
else
{
if(TryComp<AirlockComponent>(uid, out var airlockComponent))
_airlockSystem.SetBoltsWithAudio(uid, airlockComponent, false);
}
}
}
private void OnStateChanged(EntityUid uid, DoorSignalControlComponent door, DoorStateChangedEvent args)
{
var data = new NetworkPayload()
{
{ DoorSignalState, SignalState.Momentary }
};
if (args.State == DoorState.Closed)
{
data[DoorSignalState] = SignalState.Low;
_signalSystem.InvokePort(uid, door.OutOpen, data);
}
else if (args.State == DoorState.Open
|| args.State == DoorState.Opening
|| args.State == DoorState.Closing
|| args.State == DoorState.Emagging)
{
data[DoorSignalState] = SignalState.High;
_signalSystem.InvokePort(uid, door.OutOpen, data);
} }
} }
} }

View File

@@ -1,8 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Access; using Content.Server.Access;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.MachineLinking.System;
using Content.Server.Tools.Systems; using Content.Server.Tools.Systems;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
@@ -30,9 +30,7 @@ public sealed class DoorSystem : SharedDoorSystem
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly AirlockSystem _airlock = default!; [Dependency] private readonly AirlockSystem _airlock = default!;
[Dependency] private readonly AirtightSystem _airtightSystem = default!; [Dependency] private readonly AirtightSystem _airtightSystem = default!;
[Dependency] private readonly ConstructionSystem _constructionSystem = default!;
[Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize() public override void Initialize()
{ {

View File

@@ -0,0 +1,28 @@
using Content.Server.MachineLinking.Events;
using Content.Shared.MachineLinking;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.MachineLinking.Components;
[RegisterComponent]
public sealed class OrGateComponent : Component
{
// Initial state
[ViewVariables]
public SignalState StateA1 = SignalState.Low;
[ViewVariables]
public SignalState StateB1 = SignalState.Low;
[ViewVariables]
public SignalState LastO1 = SignalState.Low;
[ViewVariables]
public SignalState StateA2 = SignalState.Low;
[ViewVariables]
public SignalState StateB2 = SignalState.Low;
[ViewVariables]
public SignalState LastO2 = SignalState.Low;
}

View File

@@ -20,5 +20,16 @@ namespace Content.Server.MachineLinking.Components
[DataField("requiredQuality", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))] [DataField("requiredQuality", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public string? RequiredQuality; public string? RequiredQuality;
// Utility functions below to deal with linking entities with both Transmit and Receive components.
public bool LinkTX()
{
return SavedTransmitter == null;
}
public bool LinkRX()
{
return SavedTransmitter != null && SavedReceiver == null;
}
} }
} }

View File

@@ -1,3 +1,4 @@
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System; using Content.Server.MachineLinking.System;
namespace Content.Server.MachineLinking.Components namespace Content.Server.MachineLinking.Components
@@ -31,6 +32,13 @@ namespace Content.Server.MachineLinking.Components
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float TransmissionRange = 30f; public float TransmissionRange = 30f;
/*
* Remember last output state to avoid re-raising a SignalChangedEvent if the signal
* level hasn't actually changed.
*/
[ViewVariables(VVAccess.ReadWrite)]
public SignalState LastState = SignalState.Low;
[DataField("outputs")] [DataField("outputs")]
[Access(typeof(SignalLinkerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [Access(typeof(SignalLinkerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public Dictionary<string, List<PortIdentifier>> Outputs = new(); public Dictionary<string, List<PortIdentifier>> Outputs = new();

View File

@@ -0,0 +1,23 @@
namespace Content.Server.MachineLinking.Events
{
public enum SignalState
{
Momentary, // Instantaneous pulse high, compatibility behavior
Low,
High
}
public sealed class SignalReceivedEvent : EntityEventArgs
{
public readonly string Port;
public readonly SignalState State;
public readonly EntityUid? Trigger;
public SignalReceivedEvent(string port, EntityUid? trigger, SignalState state)
{
Port = port;
Trigger = trigger;
State = state;
}
}
}

View File

@@ -0,0 +1,63 @@
using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using JetBrains.Annotations;
namespace Content.Server.MachineLinking.System
{
[UsedImplicitly]
public sealed class OrGateSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OrGateComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<OrGateComponent, SignalReceivedEvent>(OnSignalReceived);
}
private void OnInit(EntityUid uid, OrGateComponent component, ComponentInit args)
{
_signalSystem.EnsureReceiverPorts(uid, "A1", "B1", "A2", "B2");
_signalSystem.EnsureTransmitterPorts(uid, "O1", "O2");
}
private void OnSignalReceived(EntityUid uid, OrGateComponent component, SignalReceivedEvent args)
{
if (args.Port == "A1")
{
component.StateA1 = args.State;
}
else if (args.Port == "B1")
{
component.StateB1 = args.State;
}
else if (args.Port == "A2")
{
component.StateA2 = args.State;
}
else if (args.Port == "B2")
{
component.StateB2 = args.State;
}
// O1 = A1 || B1
var v1 = SignalState.Low;
if (component.StateA1 == SignalState.High || component.StateB1 == SignalState.High)
v1 = SignalState.High;
if (v1 != component.LastO1)
_signalSystem.InvokePort(uid, "O1", v1);
component.LastO1 = v1;
// O2 = A2 || B2
var v2 = SignalState.Low;
if (component.StateA2 == SignalState.High || component.StateB2 == SignalState.High)
v2 = SignalState.High;
if (v2 != component.LastO2)
_signalSystem.InvokePort(uid, "O2", v2);
component.LastO2 = v2;
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Linq; using System.Linq;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Server.DeviceLinking.Events;
using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Tools; using Content.Server.Tools;
using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceLinking.Events;
@@ -11,6 +11,7 @@ using Content.Shared.Popups;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
namespace Content.Server.MachineLinking.System namespace Content.Server.MachineLinking.System
{ {
@@ -49,7 +50,7 @@ namespace Content.Server.MachineLinking.System
var comp = EnsureComp<SignalReceiverComponent>(uid); var comp = EnsureComp<SignalReceiverComponent>(uid);
foreach (var port in ports) foreach (var port in ports)
{ {
comp.Inputs.TryAdd(port, new()); comp.Inputs.TryAdd(port, new List<PortIdentifier>());
} }
} }
@@ -58,7 +59,7 @@ namespace Content.Server.MachineLinking.System
var comp = EnsureComp<SignalTransmitterComponent>(uid); var comp = EnsureComp<SignalTransmitterComponent>(uid);
foreach (var port in ports) foreach (var port in ports)
{ {
comp.Outputs.TryAdd(port, new()); comp.Outputs.TryAdd(port, new List<PortIdentifier>());
} }
} }
@@ -72,9 +73,11 @@ namespace Content.Server.MachineLinking.System
if (!TryComp(args.Using, out SignalLinkerComponent? linker) || if (!TryComp(args.Using, out SignalLinkerComponent? linker) ||
!IsLinkerInteractable(args.Using.Value, linker)) !IsLinkerInteractable(args.Using.Value, linker))
{
return; return;
}
AlternativeVerb verb = new() var verb = new AlternativeVerb()
{ {
Text = Loc.GetString("signal-linking-verb-text-link-default"), Text = Loc.GetString("signal-linking-verb-text-link-default"),
IconEntity = args.Using IconEntity = args.Using
@@ -130,13 +133,25 @@ namespace Content.Server.MachineLinking.System
} }
public void InvokePort(EntityUid uid, string port, SignalTransmitterComponent? component = null) public void InvokePort(EntityUid uid, string port, SignalTransmitterComponent? component = null)
{
InvokePort(uid, port, SignalState.Momentary, component);
}
public void InvokePort(EntityUid uid, string port, SignalState state, SignalTransmitterComponent? component = null)
{ {
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
if (state != SignalState.Momentary && state == component.LastState)
{
// no change in output signal
return;
}
if (!component.Outputs.TryGetValue(port, out var receivers)) if (!component.Outputs.TryGetValue(port, out var receivers))
return; return;
component.LastState = state;
foreach (var receiver in receivers) foreach (var receiver in receivers)
{ {
var eventArgs = new SignalReceivedEvent(receiver.Port, uid); var eventArgs = new SignalReceivedEvent(receiver.Port, uid);
@@ -149,6 +164,7 @@ namespace Content.Server.MachineLinking.System
// validate links // validate links
Dictionary<EntityUid, SignalReceiverComponent?> uidCache = new(); Dictionary<EntityUid, SignalReceiverComponent?> uidCache = new();
foreach (var tport in transmitter.Outputs) foreach (var tport in transmitter.Outputs)
{
foreach (var rport in tport.Value) foreach (var rport in tport.Value)
{ {
if (!uidCache.TryGetValue(rport.Uid, out var receiver)) if (!uidCache.TryGetValue(rport.Uid, out var receiver))
@@ -158,6 +174,7 @@ namespace Content.Server.MachineLinking.System
else if (!rpv.Contains(new(uid, tport.Key))) else if (!rpv.Contains(new(uid, tport.Key)))
rpv.Add(new(uid, tport.Key)); rpv.Add(new(uid, tport.Key));
} }
}
} }
private void OnReceiverStartup(EntityUid uid, SignalReceiverComponent receiver, ComponentStartup args) private void OnReceiverStartup(EntityUid uid, SignalReceiverComponent receiver, ComponentStartup args)
@@ -208,12 +225,16 @@ namespace Content.Server.MachineLinking.System
private void OnTransmitterInteractUsing(EntityUid uid, SignalTransmitterComponent transmitter, InteractUsingEvent args) private void OnTransmitterInteractUsing(EntityUid uid, SignalTransmitterComponent transmitter, InteractUsingEvent args)
{ {
if (args.Handled) return; if (args.Handled)
return;
if (!TryComp(args.Used, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Used, linker) || if (!TryComp(args.Used, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Used, linker) ||
!TryComp(args.User, out ActorComponent? actor)) !TryComp(args.User, out ActorComponent? actor))
return; return;
if (!linker.LinkTX())
return;
linker.SavedTransmitter = uid; linker.SavedTransmitter = uid;
if (!TryComp(linker.SavedReceiver, out SignalReceiverComponent? receiver)) if (!TryComp(linker.SavedReceiver, out SignalReceiverComponent? receiver))
@@ -233,12 +254,16 @@ namespace Content.Server.MachineLinking.System
private void OnReceiverInteractUsing(EntityUid uid, SignalReceiverComponent receiver, InteractUsingEvent args) private void OnReceiverInteractUsing(EntityUid uid, SignalReceiverComponent receiver, InteractUsingEvent args)
{ {
if (args.Handled) return; if (args.Handled)
return;
if (!TryComp(args.Used, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Used, linker) || if (!TryComp(args.Used, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Used, linker) ||
!TryComp(args.User, out ActorComponent? actor)) !TryComp(args.User, out ActorComponent? actor))
return; return;
if (!linker.LinkRX())
return;
linker.SavedReceiver = uid; linker.SavedReceiver = uid;
if (!TryComp(linker.SavedTransmitter, out SignalTransmitterComponent? transmitter)) if (!TryComp(linker.SavedTransmitter, out SignalTransmitterComponent? transmitter))
@@ -274,10 +299,14 @@ namespace Content.Server.MachineLinking.System
var outKeys = transmitter.Outputs.Keys.ToList(); var outKeys = transmitter.Outputs.Keys.ToList();
var inKeys = receiver.Inputs.Keys.ToList(); var inKeys = receiver.Inputs.Keys.ToList();
List<(int, int)> links = new(); List<(int, int)> links = new();
for (int i = 0; i < outKeys.Count; i++) for (var i = 0; i < outKeys.Count; i++)
{
foreach (var re in transmitter.Outputs[outKeys[i]]) foreach (var re in transmitter.Outputs[outKeys[i]])
{
if (re.Uid == receiver.Owner) if (re.Uid == receiver.Owner)
links.Add((i, inKeys.IndexOf(re.Port))); links.Add((i, inKeys.IndexOf(re.Port)));
}
}
bui.SetState(new SignalPortsState($"{Name(transmitter.Owner)} ({transmitter.Owner})", outKeys, bui.SetState(new SignalPortsState($"{Name(transmitter.Owner)} ({transmitter.Owner})", outKeys,
$"{Name(receiver.Owner)} ({receiver.Owner})", inKeys, links)); $"{Name(receiver.Owner)} ({receiver.Owner})", inKeys, links));
@@ -289,7 +318,9 @@ namespace Content.Server.MachineLinking.System
{ {
if (!transmitter.Outputs.TryGetValue(args.TransmitterPort, out var linkedReceivers) || if (!transmitter.Outputs.TryGetValue(args.TransmitterPort, out var linkedReceivers) ||
!receiver.Inputs.TryGetValue(args.ReceiverPort, out var linkedTransmitters)) !receiver.Inputs.TryGetValue(args.ReceiverPort, out var linkedTransmitters))
{
return false; return false;
}
quiet |= !user.HasValue; quiet |= !user.HasValue;
@@ -328,10 +359,12 @@ namespace Content.Server.MachineLinking.System
linkedReceivers.Add(new(receiver.Owner, args.ReceiverPort)); linkedReceivers.Add(new(receiver.Owner, args.ReceiverPort));
linkedTransmitters.Add(new(transmitter.Owner, args.TransmitterPort)); linkedTransmitters.Add(new(transmitter.Owner, args.TransmitterPort));
if (!quiet) if (!quiet)
{
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-linked-port", _popupSystem.PopupCursor(Loc.GetString("signal-linker-component-linked-port",
("machine1", transmitter.Owner), ("port1", PortName<TransmitterPortPrototype>(args.TransmitterPort)), ("machine1", transmitter.Owner), ("port1", PortName<TransmitterPortPrototype>(args.TransmitterPort)),
("machine2", receiver.Owner), ("port2", PortName<ReceiverPortPrototype>(args.ReceiverPort))), ("machine2", receiver.Owner), ("port2", PortName<ReceiverPortPrototype>(args.ReceiverPort))),
user!.Value, PopupType.Medium); user!.Value, PopupType.Medium);
}
var newLink = new NewLinkEvent(user, transmitter.Owner, args.TransmitterPort, receiver.Owner, args.ReceiverPort); var newLink = new NewLinkEvent(user, transmitter.Owner, args.TransmitterPort, receiver.Owner, args.ReceiverPort);
RaiseLocalEvent(receiver.Owner, newLink); RaiseLocalEvent(receiver.Owner, newLink);
@@ -353,12 +386,14 @@ namespace Content.Server.MachineLinking.System
if (receivers.Contains(new(receiver.Owner, args.ReceiverPort)) || if (receivers.Contains(new(receiver.Owner, args.ReceiverPort)) ||
transmitters.Contains(new(transmitter.Owner, args.TransmitterPort))) transmitters.Contains(new(transmitter.Owner, args.TransmitterPort)))
{ // link already exists, remove it {
// link already exists, remove it
if (receivers.Remove(new(receiver.Owner, args.ReceiverPort)) && if (receivers.Remove(new(receiver.Owner, args.ReceiverPort)) &&
transmitters.Remove(new(transmitter.Owner, args.TransmitterPort))) transmitters.Remove(new(transmitter.Owner, args.TransmitterPort)))
{ {
RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(args.ReceiverPort), true); RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(args.ReceiverPort), true);
RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(args.TransmitterPort), true); RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(args.TransmitterPort), true);
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-unlinked-port", _popupSystem.PopupCursor(Loc.GetString("signal-linker-component-unlinked-port",
("machine1", transmitter.Owner), ("port1", PortName<TransmitterPortPrototype>(args.TransmitterPort)), ("machine1", transmitter.Owner), ("port1", PortName<TransmitterPortPrototype>(args.TransmitterPort)),
("machine2", receiver.Owner), ("port2", PortName<ReceiverPortPrototype>(args.ReceiverPort))), ("machine2", receiver.Owner), ("port2", PortName<ReceiverPortPrototype>(args.ReceiverPort))),
@@ -397,12 +432,16 @@ namespace Content.Server.MachineLinking.System
return; return;
foreach (var (port, receivers) in transmitter.Outputs) foreach (var (port, receivers) in transmitter.Outputs)
{
if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0) if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0)
RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port), true); RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port), true);
}
foreach (var (port, transmitters) in receiver.Inputs) foreach (var (port, transmitters) in receiver.Inputs)
{
if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0) if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0)
RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port), true); RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port), true);
}
TryUpdateUI(linker, transmitter, receiver); TryUpdateUI(linker, transmitter, receiver);
} }
@@ -437,12 +476,16 @@ namespace Content.Server.MachineLinking.System
// First, disconnect existing links. // First, disconnect existing links.
foreach (var (port, receivers) in transmitter.Outputs) foreach (var (port, receivers) in transmitter.Outputs)
{
if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0) if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0)
RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port), true); RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port), true);
}
foreach (var (port, transmitters) in receiver.Inputs) foreach (var (port, transmitters) in receiver.Inputs)
{
if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0) if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0)
RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port), true); RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port), true);
}
// Then make any valid default connections. // Then make any valid default connections.
foreach (var outPort in transmitter.Outputs.Keys) foreach (var outPort in transmitter.Outputs.Keys)
@@ -474,6 +517,7 @@ namespace Content.Server.MachineLinking.System
transmitterPower.Provider?.Net == receiverPower.Provider?.Net) transmitterPower.Provider?.Net == receiverPower.Provider?.Net)
return true; return true;
// TODO: As elsewhere don't use mappos inrange.
return Comp<TransformComponent>(transmitterComponent.Owner).MapPosition.InRange( return Comp<TransformComponent>(transmitterComponent.Owner).MapPosition.InRange(
Comp<TransformComponent>(receiverComponent.Owner).MapPosition, transmitterComponent.TransmissionRange); Comp<TransformComponent>(receiverComponent.Owner).MapPosition, transmitterComponent.TransmissionRange);
} }

View File

@@ -442,6 +442,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem
private bool InRange(EntityUid sourceUid, EntityUid sinkUid, float range) private bool InRange(EntityUid sourceUid, EntityUid sinkUid, float range)
{ {
// TODO: This should be using an existing method and also coordinates inrange instead.
return Transform(sourceUid).MapPosition.InRange(Transform(sinkUid).MapPosition, range); return Transform(sourceUid).MapPosition.InRange(Transform(sinkUid).MapPosition, range);
} }

View File

@@ -131,6 +131,10 @@ public abstract class SharedDoorSystem : EntitySystem
if (!Resolve(uid, ref door)) if (!Resolve(uid, ref door))
return; return;
// If no change, return to avoid firing a new DoorStateChangedEvent.
if (state == door.State)
return;
switch (state) switch (state)
{ {
case DoorState.Opening: case DoorState.Opening:

View File

@@ -1,5 +1,5 @@
signal-port-name-toggle = Autoclose signal-port-name-autoclose = Autoclose
signal-port-description-toggle = Toggles whether the device should automatically close. signal-port-description-autoclose = Toggles whether the device should automatically close.
signal-port-name-toggle = Toggle signal-port-name-toggle = Toggle
signal-port-description-toggle = Toggles the state of a device. signal-port-description-toggle = Toggles the state of a device.
@@ -22,6 +22,9 @@ signal-port-description-open = Opens a device.
signal-port-name-close = Close signal-port-name-close = Close
signal-port-description-close = Closes a device. signal-port-description-close = Closes a device.
signal-port-name-doorbolt = Door bolt
signal-port-description-doorbolt = Toggles door bolt.
signal-port-name-trigger = Trigger signal-port-name-trigger = Trigger
signal-port-description-trigger = Triggers some mechanism on the device. signal-port-description-trigger = Triggers some mechanism on the device.

View File

@@ -13,6 +13,9 @@ signal-port-description-left = This port is invoked whenever the lever is moved
signal-port-name-right = Right signal-port-name-right = Right
signal-port-description-right = This port is invoked whenever the lever is moved to the rightmost position. signal-port-description-right = This port is invoked whenever the lever is moved to the rightmost position.
signal-port-name-doorstatus = Door status
signal-port-description-doorstatus = This port is invoked whenever the door's status changes.
signal-port-name-middle = Middle signal-port-name-middle = Middle
signal-port-description-middle = This port is invoked whenever the lever is moved to the neutral position. signal-port-description-middle = This port is invoked whenever the lever is moved to the neutral position.

View File

@@ -38,6 +38,11 @@
name: signal-port-name-close name: signal-port-name-close
description: signal-port-description-close description: signal-port-description-close
- type: sinkPort
id: DoorBolt
name: signal-port-name-doorbolt
description: signal-port-description-doorbolt
- type: sinkPort - type: sinkPort
id: Trigger id: Trigger
name: signal-port-name-trigger name: signal-port-name-trigger

View File

@@ -34,6 +34,11 @@
description: signal-port-description-middle description: signal-port-description-middle
defaultLinks: [ Off, Close ] defaultLinks: [ Off, Close ]
- type: sourcePort
id: DoorStatus
name: signal-port-name-doorstatus
description: signal-port-description-status
- type: sourcePort - type: sourcePort
id: OrderSender id: OrderSender
name: signal-port-name-order-sender name: signal-port-name-order-sender

View File

@@ -89,6 +89,10 @@
- Close - Close
- Toggle - Toggle
- AutoClose - AutoClose
- DoorBolt
- type: DeviceLinkSource
ports:
- DoorStatus
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.WiresUiKey.Key - key: enum.WiresUiKey.Key

View File

@@ -0,0 +1,26 @@
- type: entity
id: OrGate
name: MS7432
description: Dual 2-Input OR Gate
parent: BaseItem
placement:
mode: SnapgridCenter
snap:
- Wallmount
components:
- type: Anchorable
- type: Sprite
sprite: Objects/Devices/gates.rsi
state: or
- type: Rotatable
- type: OrGate
- type: SignalReceiver
inputs:
A1: []
B1: []
A2: []
B2: []
- type: SignalTransmitter
outputs:
O1: []
O2: []

View File

@@ -72,3 +72,28 @@
id: ArtifactAnalyzerReceiver id: ArtifactAnalyzerReceiver
name: signal-port-name-artifact-analyzer-receiver name: signal-port-name-artifact-analyzer-receiver
description: signal-port-description-artifact-analyzer-receiver description: signal-port-description-artifact-analyzer-receiver
- type: receiverPort
id: DoorBolt
name: "Bolt"
description: "Bolt door when HIGH."
- type: receiverPort
id: A1
name: "Input A1"
description: "Input A1"
- type: receiverPort
id: B1
name: "Input B1"
description: "Input B1"
- type: receiverPort
id: A2
name: "Input A2"
description: "Input A2"
- type: receiverPort
id: B2
name: "Input B2"
description: "Input B2"

View File

@@ -67,3 +67,18 @@
name: signal-port-name-artifact-analyzer-sender name: signal-port-name-artifact-analyzer-sender
description: signal-port-description-artifact-analyzer-sender description: signal-port-description-artifact-analyzer-sender
defaultLinks: [ ArtifactAnalyzerReceiver ] defaultLinks: [ ArtifactAnalyzerReceiver ]
- type: transmitterPort
id: DoorStatus
name: "Door Status"
description: "HIGH when door is open, LOW when door is closed."
- type: transmitterPort
id: O1
name: "Output 1"
description: "Output 1"
- type: transmitterPort
id: O2
name: "Output 2"
description: "Output 2"

View File

@@ -0,0 +1,14 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Kevin Zheng 2022",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "or"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB