diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index eda8e10ae2..15a1080967 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -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. "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] diff --git a/Content.Server/DeviceLinking/Components/DoorSignalControlComponent.cs b/Content.Server/DeviceLinking/Components/DoorSignalControlComponent.cs index 98c8792f77..b19fdb5f4d 100644 --- a/Content.Server/DeviceLinking/Components/DoorSignalControlComponent.cs +++ b/Content.Server/DeviceLinking/Components/DoorSignalControlComponent.cs @@ -14,5 +14,11 @@ namespace Content.Server.DeviceLinking.Components [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer))] public string TogglePort = "Toggle"; + + [DataField("boltPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string InBolt = "DoorBolt"; + + [DataField("onOpenPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OutOpen = "DoorStatus"; } } diff --git a/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs b/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs index 930beee53b..969161c166 100644 --- a/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs +++ b/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs @@ -1,28 +1,37 @@ using Content.Server.DeviceLinking.Components; -using Content.Server.DeviceLinking.Events; +using Content.Server.DeviceNetwork; using Content.Server.Doors.Systems; +using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.System; using Content.Shared.Doors.Components; +using Content.Shared.Doors; using JetBrains.Annotations; +using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent; namespace Content.Server.DeviceLinking.Systems { [UsedImplicitly] public sealed class DoorSignalControlSystem : EntitySystem { + [Dependency] private readonly AirlockSystem _airlockSystem = default!; [Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; + private const string DoorSignalState = "DoorState"; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnSignalReceived); + SubscribeLocalEvent(OnStateChanged); } private void OnInit(EntityUid uid, DoorSignalControlComponent component, ComponentInit args) { + _signalSystem.EnsureSinkPorts(uid, component.OpenPort, component.ClosePort, component.TogglePort); + _signalSystem.EnsureSourcePorts(uid, component.OutOpen); } 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)) return; + var state = SignalState.Momentary; + args.Data?.TryGetValue(DoorSignalState, out state); + + if (args.Port == component.OpenPort) { - if (door.State != DoorState.Open) - _doorSystem.TryOpen(uid, door); + if (state == SignalState.High || state == SignalState.Momentary) + { + if (door.State != DoorState.Open) + _doorSystem.TryOpen(uid, door); + } } else if (args.Port == component.ClosePort) { - if (door.State != DoorState.Closed) - _doorSystem.TryClose(uid, door); + if (state == SignalState.High || state == SignalState.Momentary) + { + if (door.State != DoorState.Closed) + _doorSystem.TryClose(uid, door); + } } 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(uid, out var airlockComponent)) + _airlockSystem.SetBoltsWithAudio(uid, airlockComponent, true); + } + else + { + if(TryComp(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); } } } diff --git a/Content.Server/Doors/Systems/DoorSystem.cs b/Content.Server/Doors/Systems/DoorSystem.cs index 7b34a3c08b..b7ac9086f6 100644 --- a/Content.Server/Doors/Systems/DoorSystem.cs +++ b/Content.Server/Doors/Systems/DoorSystem.cs @@ -1,8 +1,8 @@ -using System.Diagnostics.CodeAnalysis; using Content.Server.Access; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Construction; +using Content.Server.MachineLinking.System; using Content.Server.Tools.Systems; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; @@ -30,9 +30,7 @@ public sealed class DoorSystem : SharedDoorSystem [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly AirlockSystem _airlock = default!; [Dependency] private readonly AirtightSystem _airtightSystem = default!; - [Dependency] private readonly ConstructionSystem _constructionSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; public override void Initialize() { diff --git a/Content.Server/MachineLinking/Components/OrGateComponent.cs b/Content.Server/MachineLinking/Components/OrGateComponent.cs new file mode 100644 index 0000000000..2e088b7e71 --- /dev/null +++ b/Content.Server/MachineLinking/Components/OrGateComponent.cs @@ -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; +} diff --git a/Content.Server/MachineLinking/Components/SignalLinkerComponent.cs b/Content.Server/MachineLinking/Components/SignalLinkerComponent.cs index b87a6970fb..a3ed0a4a13 100644 --- a/Content.Server/MachineLinking/Components/SignalLinkerComponent.cs +++ b/Content.Server/MachineLinking/Components/SignalLinkerComponent.cs @@ -20,5 +20,16 @@ namespace Content.Server.MachineLinking.Components [DataField("requiredQuality", customTypeSerializer: typeof(PrototypeIdSerializer))] [ViewVariables(VVAccess.ReadWrite)] 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; + } } } diff --git a/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs b/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs index 555fa76802..b3b48691a0 100644 --- a/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs +++ b/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs @@ -1,3 +1,4 @@ +using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.System; namespace Content.Server.MachineLinking.Components @@ -31,6 +32,13 @@ namespace Content.Server.MachineLinking.Components [ViewVariables(VVAccess.ReadWrite)] 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")] [Access(typeof(SignalLinkerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends public Dictionary> Outputs = new(); diff --git a/Content.Server/MachineLinking/Events/SignalReceivedEvent.cs b/Content.Server/MachineLinking/Events/SignalReceivedEvent.cs index e69de29bb2..40d8e07c97 100644 --- a/Content.Server/MachineLinking/Events/SignalReceivedEvent.cs +++ b/Content.Server/MachineLinking/Events/SignalReceivedEvent.cs @@ -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; + } + } +} diff --git a/Content.Server/MachineLinking/System/OrGateSystem.cs b/Content.Server/MachineLinking/System/OrGateSystem.cs new file mode 100644 index 0000000000..af5d9fb26f --- /dev/null +++ b/Content.Server/MachineLinking/System/OrGateSystem.cs @@ -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(OnInit); + SubscribeLocalEvent(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; + } + } +} diff --git a/Content.Server/MachineLinking/System/SignalLinkerSystem.cs b/Content.Server/MachineLinking/System/SignalLinkerSystem.cs index 811a3f771a..6743919077 100644 --- a/Content.Server/MachineLinking/System/SignalLinkerSystem.cs +++ b/Content.Server/MachineLinking/System/SignalLinkerSystem.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Diagnostics.CodeAnalysis; -using Content.Server.DeviceLinking.Events; using Content.Server.MachineLinking.Components; +using Content.Server.MachineLinking.Events; using Content.Server.Power.Components; using Content.Server.Tools; using Content.Shared.DeviceLinking.Events; @@ -11,6 +11,7 @@ using Content.Shared.Popups; using Robust.Server.GameObjects; using Content.Shared.Verbs; using Robust.Shared.Prototypes; +using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent; namespace Content.Server.MachineLinking.System { @@ -49,7 +50,7 @@ namespace Content.Server.MachineLinking.System var comp = EnsureComp(uid); foreach (var port in ports) { - comp.Inputs.TryAdd(port, new()); + comp.Inputs.TryAdd(port, new List()); } } @@ -58,7 +59,7 @@ namespace Content.Server.MachineLinking.System var comp = EnsureComp(uid); foreach (var port in ports) { - comp.Outputs.TryAdd(port, new()); + comp.Outputs.TryAdd(port, new List()); } } @@ -72,9 +73,11 @@ namespace Content.Server.MachineLinking.System if (!TryComp(args.Using, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Using.Value, linker)) + { return; + } - AlternativeVerb verb = new() + var verb = new AlternativeVerb() { Text = Loc.GetString("signal-linking-verb-text-link-default"), IconEntity = args.Using @@ -130,13 +133,25 @@ namespace Content.Server.MachineLinking.System } 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)) return; + if (state != SignalState.Momentary && state == component.LastState) + { + // no change in output signal + return; + } + if (!component.Outputs.TryGetValue(port, out var receivers)) return; + component.LastState = state; foreach (var receiver in receivers) { var eventArgs = new SignalReceivedEvent(receiver.Port, uid); @@ -149,6 +164,7 @@ namespace Content.Server.MachineLinking.System // validate links Dictionary uidCache = new(); foreach (var tport in transmitter.Outputs) + { foreach (var rport in tport.Value) { 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))) rpv.Add(new(uid, tport.Key)); } + } } 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) { - if (args.Handled) return; + if (args.Handled) + return; if (!TryComp(args.Used, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Used, linker) || !TryComp(args.User, out ActorComponent? actor)) return; + if (!linker.LinkTX()) + return; + linker.SavedTransmitter = uid; 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) { - if (args.Handled) return; + if (args.Handled) + return; if (!TryComp(args.Used, out SignalLinkerComponent? linker) || !IsLinkerInteractable(args.Used, linker) || !TryComp(args.User, out ActorComponent? actor)) return; + if (!linker.LinkRX()) + return; + linker.SavedReceiver = uid; if (!TryComp(linker.SavedTransmitter, out SignalTransmitterComponent? transmitter)) @@ -274,10 +299,14 @@ namespace Content.Server.MachineLinking.System var outKeys = transmitter.Outputs.Keys.ToList(); var inKeys = receiver.Inputs.Keys.ToList(); 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]]) + { if (re.Uid == receiver.Owner) links.Add((i, inKeys.IndexOf(re.Port))); + } + } bui.SetState(new SignalPortsState($"{Name(transmitter.Owner)} ({transmitter.Owner})", outKeys, $"{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) || !receiver.Inputs.TryGetValue(args.ReceiverPort, out var linkedTransmitters)) + { return false; + } quiet |= !user.HasValue; @@ -328,10 +359,12 @@ namespace Content.Server.MachineLinking.System linkedReceivers.Add(new(receiver.Owner, args.ReceiverPort)); linkedTransmitters.Add(new(transmitter.Owner, args.TransmitterPort)); if (!quiet) + { _popupSystem.PopupCursor(Loc.GetString("signal-linker-component-linked-port", - ("machine1", transmitter.Owner), ("port1", PortName(args.TransmitterPort)), - ("machine2", receiver.Owner), ("port2", PortName(args.ReceiverPort))), + ("machine1", transmitter.Owner), ("port1", PortName(args.TransmitterPort)), + ("machine2", receiver.Owner), ("port2", PortName(args.ReceiverPort))), user!.Value, PopupType.Medium); + } var newLink = new NewLinkEvent(user, transmitter.Owner, args.TransmitterPort, receiver.Owner, args.ReceiverPort); RaiseLocalEvent(receiver.Owner, newLink); @@ -353,12 +386,14 @@ namespace Content.Server.MachineLinking.System if (receivers.Contains(new(receiver.Owner, args.ReceiverPort)) || 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)) && transmitters.Remove(new(transmitter.Owner, args.TransmitterPort))) { RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(args.ReceiverPort), true); RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(args.TransmitterPort), true); + _popupSystem.PopupCursor(Loc.GetString("signal-linker-component-unlinked-port", ("machine1", transmitter.Owner), ("port1", PortName(args.TransmitterPort)), ("machine2", receiver.Owner), ("port2", PortName(args.ReceiverPort))), @@ -397,12 +432,16 @@ namespace Content.Server.MachineLinking.System return; foreach (var (port, receivers) in transmitter.Outputs) + { if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0) RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port), true); + } foreach (var (port, transmitters) in receiver.Inputs) + { if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0) RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port), true); + } TryUpdateUI(linker, transmitter, receiver); } @@ -437,12 +476,16 @@ namespace Content.Server.MachineLinking.System // First, disconnect existing links. foreach (var (port, receivers) in transmitter.Outputs) + { if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0) RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port), true); + } foreach (var (port, transmitters) in receiver.Inputs) + { if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0) RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port), true); + } // Then make any valid default connections. foreach (var outPort in transmitter.Outputs.Keys) @@ -474,6 +517,7 @@ namespace Content.Server.MachineLinking.System transmitterPower.Provider?.Net == receiverPower.Provider?.Net) return true; + // TODO: As elsewhere don't use mappos inrange. return Comp(transmitterComponent.Owner).MapPosition.InRange( Comp(receiverComponent.Owner).MapPosition, transmitterComponent.TransmissionRange); } diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index b6b784b63c..648596ffc1 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -442,6 +442,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem 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); } diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index cf52ee5e3e..b4828d5e79 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -131,6 +131,10 @@ public abstract class SharedDoorSystem : EntitySystem if (!Resolve(uid, ref door)) return; + // If no change, return to avoid firing a new DoorStateChangedEvent. + if (state == door.State) + return; + switch (state) { case DoorState.Opening: diff --git a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl index cbf2f537f9..723a733b31 100644 --- a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl +++ b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl @@ -1,5 +1,5 @@ -signal-port-name-toggle = Autoclose -signal-port-description-toggle = Toggles whether the device should automatically close. +signal-port-name-autoclose = Autoclose +signal-port-description-autoclose = Toggles whether the device should automatically close. signal-port-name-toggle = Toggle 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-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-description-trigger = Triggers some mechanism on the device. diff --git a/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl b/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl index 03fae00159..2b61bd4e11 100644 --- a/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl +++ b/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl @@ -13,6 +13,9 @@ signal-port-description-left = This port is invoked whenever the lever is moved signal-port-name-right = Right 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-description-middle = This port is invoked whenever the lever is moved to the neutral position. diff --git a/Resources/Prototypes/DeviceLinking/sink_ports.yml b/Resources/Prototypes/DeviceLinking/sink_ports.yml index 19f3b4e1c7..37817eddaa 100644 --- a/Resources/Prototypes/DeviceLinking/sink_ports.yml +++ b/Resources/Prototypes/DeviceLinking/sink_ports.yml @@ -38,6 +38,11 @@ name: signal-port-name-close description: signal-port-description-close +- type: sinkPort + id: DoorBolt + name: signal-port-name-doorbolt + description: signal-port-description-doorbolt + - type: sinkPort id: Trigger name: signal-port-name-trigger diff --git a/Resources/Prototypes/DeviceLinking/source_ports.yml b/Resources/Prototypes/DeviceLinking/source_ports.yml index d01089205c..02f55f1798 100644 --- a/Resources/Prototypes/DeviceLinking/source_ports.yml +++ b/Resources/Prototypes/DeviceLinking/source_ports.yml @@ -34,6 +34,11 @@ description: signal-port-description-middle defaultLinks: [ Off, Close ] +- type: sourcePort + id: DoorStatus + name: signal-port-name-doorstatus + description: signal-port-description-status + - type: sourcePort id: OrderSender name: signal-port-name-order-sender diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 7e8100d10a..d2c2279b2d 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -89,6 +89,10 @@ - Close - Toggle - AutoClose + - DoorBolt + - type: DeviceLinkSource + ports: + - DoorStatus - type: UserInterface interfaces: - key: enum.WiresUiKey.Key diff --git a/Resources/Prototypes/Entities/Structures/gates.yml b/Resources/Prototypes/Entities/Structures/gates.yml new file mode 100644 index 0000000000..94f0cb46ce --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/gates.yml @@ -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: [] diff --git a/Resources/Prototypes/MachineLinking/receiver_ports.yml b/Resources/Prototypes/MachineLinking/receiver_ports.yml index 923ab4d49b..9156fd2b59 100644 --- a/Resources/Prototypes/MachineLinking/receiver_ports.yml +++ b/Resources/Prototypes/MachineLinking/receiver_ports.yml @@ -72,3 +72,28 @@ id: ArtifactAnalyzerReceiver name: signal-port-name-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" diff --git a/Resources/Prototypes/MachineLinking/transmitter_ports.yml b/Resources/Prototypes/MachineLinking/transmitter_ports.yml index c4d25c6461..41a6a80307 100644 --- a/Resources/Prototypes/MachineLinking/transmitter_ports.yml +++ b/Resources/Prototypes/MachineLinking/transmitter_ports.yml @@ -67,3 +67,18 @@ name: signal-port-name-artifact-analyzer-sender description: signal-port-description-artifact-analyzer-sender 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" diff --git a/Resources/Textures/Objects/Devices/gates.rsi/meta.json b/Resources/Textures/Objects/Devices/gates.rsi/meta.json new file mode 100644 index 0000000000..b71ae48098 --- /dev/null +++ b/Resources/Textures/Objects/Devices/gates.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Kevin Zheng 2022", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "or" + } + ] +} diff --git a/Resources/Textures/Objects/Devices/gates.rsi/or.png b/Resources/Textures/Objects/Devices/gates.rsi/or.png new file mode 100644 index 0000000000..513d4f5aee Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/or.png differ