diff --git a/Content.Server/DeviceLinking/Components/EdgeDetectorComponent.cs b/Content.Server/DeviceLinking/Components/EdgeDetectorComponent.cs
new file mode 100644
index 0000000000..8e372302fc
--- /dev/null
+++ b/Content.Server/DeviceLinking/Components/EdgeDetectorComponent.cs
@@ -0,0 +1,36 @@
+using Content.Server.DeviceLinking.Systems;
+using Content.Shared.DeviceLinking;
+using Content.Shared.MachineLinking;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.DeviceLinking.Components;
+
+///
+/// An edge detector that pulses high or low output ports when the input port gets a rising or falling edge respectively.
+///
+[RegisterComponent]
+[Access(typeof(EdgeDetectorSystem))]
+public sealed class EdgeDetectorComponent : Component
+{
+ ///
+ /// Name of the input port.
+ ///
+ [DataField("inputPort", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string InputPort = "Input";
+
+ ///
+ /// Name of the rising edge output port.
+ ///
+ [DataField("outputHighPort", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string OutputHighPort = "OutputHigh";
+
+ ///
+ /// Name of the falling edge output port.
+ ///
+ [DataField("outputLowPort", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string OutputLowPort = "OutputLow";
+
+ // Initial state
+ [ViewVariables]
+ public SignalState State = SignalState.Low;
+}
diff --git a/Content.Server/DeviceLinking/Components/LogicGateComponent.cs b/Content.Server/DeviceLinking/Components/LogicGateComponent.cs
new file mode 100644
index 0000000000..21c9b27009
--- /dev/null
+++ b/Content.Server/DeviceLinking/Components/LogicGateComponent.cs
@@ -0,0 +1,73 @@
+using Content.Server.DeviceLinking.Systems;
+using Content.Shared.DeviceLinking;
+using Content.Shared.MachineLinking;
+using Content.Shared.Tools;
+using Robust.Shared.Audio;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.DeviceLinking.Components;
+
+///
+/// A logic gate that sets its output port by doing an operation on its 2 input ports, A and B.
+///
+[RegisterComponent]
+[Access(typeof(LogicGateSystem))]
+public sealed class LogicGateComponent : Component
+{
+ ///
+ /// The logic gate operation to use.
+ ///
+ [DataField("gate")]
+ public LogicGate Gate = LogicGate.Or;
+
+ ///
+ /// Tool quality to use for cycling logic gate operations.
+ /// Cannot be pulsing since linking uses that.
+ ///
+ [DataField("cycleQuality", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string CycleQuality = "Screwing";
+
+ ///
+ /// Sound played when cycling logic gate operations.
+ ///
+ [DataField("cycleSound")]
+ public SoundSpecifier CycleSound = new SoundPathSpecifier("/Audio/Machines/lightswitch.ogg");
+
+ ///
+ /// Name of the first input port.
+ ///
+ [DataField("inputPortA", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string InputPortA = "InputA";
+
+ ///
+ /// Name of the second input port.
+ ///
+ [DataField("inputPortB", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string InputPortB = "InputB";
+
+ ///
+ /// Name of the output port.
+ ///
+ [DataField("outputPort", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string OutputPort = "Output";
+
+ // Initial state
+ [ViewVariables]
+ public SignalState StateA = SignalState.Low;
+
+ [ViewVariables]
+ public SignalState StateB = SignalState.Low;
+
+ [ViewVariables]
+ public bool LastOutput;
+}
+
+///
+/// Last state of a signal port, used to not spam invoking ports.
+///
+public enum SignalState : byte
+{
+ Momentary, // Instantaneous pulse high, compatibility behavior
+ Low,
+ High
+}
diff --git a/Content.Server/DeviceLinking/Components/OrGateComponent.cs b/Content.Server/DeviceLinking/Components/OrGateComponent.cs
deleted file mode 100644
index f3e2f18379..0000000000
--- a/Content.Server/DeviceLinking/Components/OrGateComponent.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Content.Server.MachineLinking.Events;
-
-namespace Content.Server.DeviceLinking.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;
-}
-
-public enum SignalState
-{
- Momentary, // Instantaneous pulse high, compatibility behavior
- Low,
- High
-}
-
diff --git a/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs b/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs
index 54b5c36f35..56a754a756 100644
--- a/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs
+++ b/Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs
@@ -65,21 +65,21 @@ namespace Content.Server.DeviceLinking.Systems
}
else if (args.Port == component.InBolt)
{
- if (state == SignalState.High)
+ if (!TryComp(uid, out var bolts))
+ return;
+
+ // if its a pulse toggle, otherwise set bolts to high/low
+ bool bolt;
+ if (state == SignalState.Momentary)
{
- if(TryComp(uid, out var bolts))
- _bolts.SetBoltsWithAudio(uid, bolts, true);
- }
- else if (state == SignalState.Momentary)
- {
- if (TryComp(uid, out var bolts))
- _bolts.SetBoltsWithAudio(uid, bolts, newBolts: !bolts.BoltsDown);
+ bolt = !bolts.BoltsDown;
}
else
{
- if(TryComp(uid, out var bolts))
- _bolts.SetBoltsWithAudio(uid, bolts, false);
+ bolt = state == SignalState.High;
}
+
+ _bolts.SetBoltsWithAudio(uid, bolts, bolt);
}
}
diff --git a/Content.Server/DeviceLinking/Systems/EdgeDetectorSystem.cs b/Content.Server/DeviceLinking/Systems/EdgeDetectorSystem.cs
new file mode 100644
index 0000000000..32b0af1709
--- /dev/null
+++ b/Content.Server/DeviceLinking/Systems/EdgeDetectorSystem.cs
@@ -0,0 +1,47 @@
+using Content.Server.DeviceLinking.Components;
+using Content.Server.DeviceNetwork;
+using Content.Server.MachineLinking.Events;
+using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
+
+namespace Content.Server.DeviceLinking.Systems;
+
+public sealed class EdgeDetectorSystem : EntitySystem
+{
+ [Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnSignalReceived);
+ }
+
+ private void OnInit(EntityUid uid, EdgeDetectorComponent comp, ComponentInit args)
+ {
+ _deviceLink.EnsureSinkPorts(uid, comp.InputPort);
+ _deviceLink.EnsureSourcePorts(uid, comp.OutputHighPort, comp.OutputLowPort);
+ }
+
+ private void OnSignalReceived(EntityUid uid, EdgeDetectorComponent comp, ref SignalReceivedEvent args)
+ {
+ // only handle signals with edges
+ var state = SignalState.Momentary;
+ if (args.Data == null ||
+ !args.Data.TryGetValue(DeviceNetworkConstants.LogicState, out state) ||
+ state == SignalState.Momentary)
+ return;
+
+ if (args.Port != comp.InputPort)
+ return;
+
+ // make sure the level changed, multiple devices sending the same level are treated as one spamming
+ if (comp.State != state)
+ {
+ comp.State = state;
+
+ var port = state == SignalState.High ? comp.OutputHighPort : comp.OutputLowPort;
+ _deviceLink.InvokePort(uid, port);
+ }
+ }
+}
diff --git a/Content.Server/DeviceLinking/Systems/LogicGateSystem.cs b/Content.Server/DeviceLinking/Systems/LogicGateSystem.cs
new file mode 100644
index 0000000000..70e9ede1f2
--- /dev/null
+++ b/Content.Server/DeviceLinking/Systems/LogicGateSystem.cs
@@ -0,0 +1,133 @@
+using Content.Server.DeviceLinking.Components;
+using Content.Server.DeviceNetwork;
+using Content.Server.MachineLinking.Events;
+using Content.Shared.DeviceLinking;
+using Content.Shared.Examine;
+using Content.Shared.Interaction;
+using Content.Shared.Tools;
+using Content.Shared.Popups;
+using Robust.Shared.Audio;
+using Robust.Shared.Utility;
+using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
+
+namespace Content.Server.DeviceLinking.Systems;
+
+public sealed class LogicGateSystem : EntitySystem
+{
+ [Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedToolSystem _tool = default!;
+
+ private readonly int GateCount = Enum.GetValues(typeof(LogicGate)).Length;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnExamined);
+ SubscribeLocalEvent(OnInteractUsing);
+ SubscribeLocalEvent(OnSignalReceived);
+ }
+
+ private void OnInit(EntityUid uid, LogicGateComponent comp, ComponentInit args)
+ {
+ _deviceLink.EnsureSinkPorts(uid, comp.InputPortA, comp.InputPortB);
+ _deviceLink.EnsureSourcePorts(uid, comp.OutputPort);
+ }
+
+ private void OnExamined(EntityUid uid, LogicGateComponent comp, ExaminedEvent args)
+ {
+ if (!args.IsInDetailsRange)
+ return;
+
+ args.PushMarkup(Loc.GetString("logic-gate-examine", ("gate", comp.Gate.ToString().ToUpper())));
+ }
+
+ private void OnInteractUsing(EntityUid uid, LogicGateComponent comp, InteractUsingEvent args)
+ {
+ if (args.Handled || !_tool.HasQuality(args.Used, comp.CycleQuality))
+ return;
+
+ // cycle through possible gates
+ var gate = (int) comp.Gate;
+ gate = ++gate % GateCount;
+ comp.Gate = (LogicGate) gate;
+
+ // since gate changed the output probably has too, update it
+ UpdateOutput(uid, comp);
+
+ // notify the user
+ _audio.PlayPvs(comp.CycleSound, uid);
+ var msg = Loc.GetString("logic-gate-cycle", ("gate", comp.Gate.ToString().ToUpper()));
+ _popup.PopupEntity(msg, uid, args.User);
+ _appearance.SetData(uid, LogicGateVisuals.Gate, comp.Gate);
+ }
+
+ private void OnSignalReceived(EntityUid uid, LogicGateComponent comp, ref SignalReceivedEvent args)
+ {
+ // default to momentary for compatibility with non-logic signals.
+ // currently only door status and logic gates have logic signal state.
+ var state = SignalState.Momentary;
+ args.Data?.TryGetValue(DeviceNetworkConstants.LogicState, out state);
+
+ // update the state for the correct port
+ if (args.Port == comp.InputPortA)
+ {
+ comp.StateA = state;
+ }
+ else if (args.Port == comp.InputPortB)
+ {
+ comp.StateB = state;
+ }
+
+ UpdateOutput(uid, comp);
+ }
+
+ ///
+ /// Handle the logic for a logic gate, invoking the port if the output changed.
+ ///
+ private void UpdateOutput(EntityUid uid, LogicGateComponent comp)
+ {
+ // get the new output value now that it's changed
+ var a = comp.StateA == SignalState.High;
+ var b = comp.StateB == SignalState.High;
+ var output = false;
+ switch (comp.Gate)
+ {
+ case LogicGate.Or:
+ output = a || b;
+ break;
+ case LogicGate.And:
+ output = a && b;
+ break;
+ case LogicGate.Xor:
+ output = a != b;
+ break;
+ case LogicGate.Nor:
+ output = !(a || b);
+ break;
+ case LogicGate.Nand:
+ output = !(a && b);
+ break;
+ case LogicGate.Xnor:
+ output = a == b;
+ break;
+ }
+
+ // only send a payload if it actually changed
+ if (output != comp.LastOutput)
+ {
+ comp.LastOutput = output;
+
+ var data = new NetworkPayload
+ {
+ [DeviceNetworkConstants.LogicState] = output ? SignalState.High : SignalState.Low
+ };
+
+ _deviceLink.InvokePort(uid, comp.OutputPort, data);
+ }
+ }
+}
diff --git a/Content.Server/DeviceLinking/Systems/OrGateSystem.cs b/Content.Server/DeviceLinking/Systems/OrGateSystem.cs
deleted file mode 100644
index d47c281036..0000000000
--- a/Content.Server/DeviceLinking/Systems/OrGateSystem.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using Content.Server.DeviceLinking.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.MachineLinking.Events;
-using JetBrains.Annotations;
-using Robust.Shared.Utility;
-using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
-
-namespace Content.Server.DeviceLinking.Systems
-{
- [UsedImplicitly]
- public sealed class OrGateSystem : EntitySystem
- {
-
- [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent(OnInit);
- SubscribeLocalEvent(OnSignalReceived);
- }
-
- private void OnInit(EntityUid uid, OrGateComponent component, ComponentInit args)
- {
- _signalSystem.EnsureSinkPorts(uid, "A1", "B1", "A2", "B2");
- _signalSystem.EnsureSourcePorts(uid, "O1", "O2");
- }
-
- private void OnSignalReceived(EntityUid uid, OrGateComponent component, ref SignalReceivedEvent args)
- {
- var state = SignalState.Momentary;
- args.Data?.TryGetValue(DeviceNetworkConstants.LogicState, out state);
-
- switch (args.Port)
- {
- case "A1":
- component.StateA1 = state;
- break;
- case "B1":
- component.StateB1 = state;
- break;
- case "A2":
- component.StateA2 = state;
- break;
- case "B2":
- component.StateB2 = state;
- break;
- }
-
- // O1 = A1 || B1
- var v1 = SignalState.Low;
- if (component.StateA1 == SignalState.High || component.StateB1 == SignalState.High)
- v1 = SignalState.High;
-
- if (v1 != component.LastO1)
- {
- var data = new NetworkPayload
- {
- [DeviceNetworkConstants.LogicState] = v1
- };
-
- _signalSystem.InvokePort(uid, "O1", data);
- }
-
- 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)
- {
- var data = new NetworkPayload
- {
- [DeviceNetworkConstants.LogicState] = v2
- };
-
- _signalSystem.InvokePort(uid, "O2", data);
- }
- component.LastO2 = v2;
- }
- }
-}
diff --git a/Content.Shared/DeviceLinking/SharedLogicGate.cs b/Content.Shared/DeviceLinking/SharedLogicGate.cs
new file mode 100644
index 0000000000..8d814f461f
--- /dev/null
+++ b/Content.Shared/DeviceLinking/SharedLogicGate.cs
@@ -0,0 +1,36 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.DeviceLinking;
+
+
+///
+/// Types of logic gates that can be used, determines how the output port is set.
+///
+[Serializable, NetSerializable]
+public enum LogicGate : byte
+{
+ Or,
+ And,
+ Xor,
+ Nor,
+ Nand,
+ Xnor
+}
+
+///
+/// Tells clients which logic gate layer to draw.
+///
+[Serializable, NetSerializable]
+public enum LogicGateVisuals : byte
+{
+ Gate
+}
+
+///
+/// Sprite layer for the logic gate.
+///
+[Serializable, NetSerializable]
+public enum LogicGateLayers : byte
+{
+ Gate
+}
diff --git a/Resources/Locale/en-US/logic-gates/logic-gates.ftl b/Resources/Locale/en-US/logic-gates/logic-gates.ftl
new file mode 100644
index 0000000000..1195670e7f
--- /dev/null
+++ b/Resources/Locale/en-US/logic-gates/logic-gates.ftl
@@ -0,0 +1,3 @@
+logic-gate-examine = It is currently {INDEFINITE($gate)} {$gate} gate.
+
+logic-gate-cycle = Switched to {INDEFINITE($gate)} {$gate} gate
diff --git a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl
index b5becc5f5d..4d2dd25af2 100644
--- a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl
+++ b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl
@@ -23,7 +23,7 @@ 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-description-doorbolt = Bolts door when HIGH.
signal-port-name-trigger = Trigger
signal-port-description-trigger = Triggers some mechanism on the device.
@@ -69,3 +69,12 @@ signal-port-description-set-particle-epsilon = Sets the type of particle this de
signal-port-name-set-particle-zeta = Set particle type: zeta
signal-port-description-set-particle-zeta = Sets the type of particle this device emits to zeta.
+
+signal-port-name-logic-input-a = Input A
+signal-port-description-logic-input-a = First input of a logic gate.
+
+signal-port-name-logic-input-b = Input B
+signal-port-description-logic-input-b = Second input of a logic gate.
+
+signal-port-name-logic-input = Input
+signal-port-description-logic-input = Input to the edge detector, cannot be a pulse signal.
diff --git a/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl b/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl
index 2b61bd4e11..909c0dae87 100644
--- a/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl
+++ b/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl
@@ -14,7 +14,7 @@ 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-description-doorstatus = This port is invoked with HIGH when the door opens and LOW when the door closes.
signal-port-name-middle = Middle
signal-port-description-middle = This port is invoked whenever the lever is moved to the neutral position.
@@ -24,3 +24,12 @@ signal-port-description-timer-trigger = This port is invoked whenever the timer
signal-port-name-timer-start = Timer Start
signal-port-description-timer-start = This port is invoked whenever the timer starts.
+
+signal-port-name-logic-output = Output
+signal-port-description-logic-output = This port is invoked with HIGH or LOW depending on the selected gate and inputs.
+
+signal-port-name-logic-output-high = High Output
+signal-port-description-logic-output-high = This port is invoked whenever the input has a rising edge.
+
+signal-port-name-logic-output-low = Low Output
+signal-port-description-logic-output-low = This port is invoked whenever the input has a falling edge.
diff --git a/Resources/Prototypes/DeviceLinking/sink_ports.yml b/Resources/Prototypes/DeviceLinking/sink_ports.yml
index 8a52ebbc0d..f05934f3ba 100644
--- a/Resources/Prototypes/DeviceLinking/sink_ports.yml
+++ b/Resources/Prototypes/DeviceLinking/sink_ports.yml
@@ -79,24 +79,19 @@
description: signal-port-description-artifact-analyzer-receiver
- type: sinkPort
- id: A1
- name: "Input A1"
- description: "Input A1"
+ id: InputA
+ name: signal-port-name-logic-input-a
+ description: signal-port-description-logic-input-a
- type: sinkPort
- id: B1
- name: "Input B1"
- description: "Input B1"
+ id: InputB
+ name: signal-port-name-logic-input-b
+ description: signal-port-description-logic-input-b
- type: sinkPort
- id: A2
- name: "Input A2"
- description: "Input A2"
-
-- type: sinkPort
- id: B2
- name: "Input B2"
- description: "Input B2"
+ id: Input
+ name: signal-port-name-logic-input
+ description: signal-port-description-logic-input
- type: sinkPort
id: SetParticleDelta
diff --git a/Resources/Prototypes/DeviceLinking/source_ports.yml b/Resources/Prototypes/DeviceLinking/source_ports.yml
index 823e4a164a..151ba9c26f 100644
--- a/Resources/Prototypes/DeviceLinking/source_ports.yml
+++ b/Resources/Prototypes/DeviceLinking/source_ports.yml
@@ -37,7 +37,8 @@
- type: sourcePort
id: DoorStatus
name: signal-port-name-doorstatus
- description: signal-port-description-status
+ description: signal-port-description-doorstatus
+ defaultLinks: [ DoorBolt ]
- type: sourcePort
id: OrderSender
@@ -74,11 +75,16 @@
defaultLinks: [ Close, Off ]
- type: sourcePort
- id: O1
- name: "Output 1"
- description: "Output 1"
+ id: Output
+ name: signal-port-name-logic-output
+ description: signal-port-description-logic-output
- type: sourcePort
- id: O2
- name: "Output 2"
- description: "Output 2"
+ id: OutputHigh
+ name: signal-port-name-logic-output-high
+ description: signal-port-description-logic-output-high
+
+- type: sourcePort
+ id: OutputLow
+ name: signal-port-name-logic-output-low
+ description: signal-port-description-logic-output-low
diff --git a/Resources/Prototypes/Entities/Structures/gates.yml b/Resources/Prototypes/Entities/Structures/gates.yml
index 6d487c338a..8e5b1595c8 100644
--- a/Resources/Prototypes/Entities/Structures/gates.yml
+++ b/Resources/Prototypes/Entities/Structures/gates.yml
@@ -1,31 +1,68 @@
- type: entity
- id: OrGate
- name: MS7432
- description: Dual 2-Input OR Gate
+ abstract: true
parent: BaseItem
- placement:
- mode: SnapgridCenter
- snap:
- - Wallmount
+ id: BaseLogicItem
components:
- - type: Anchorable
- type: Sprite
sprite: Objects/Devices/gates.rsi
- state: or
+ - type: Anchorable
- type: Rotatable
- - type: OrGate
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: BasicDevice
- type: WirelessNetworkConnection
range: 200
+
+- type: entity
+ parent: BaseLogicItem
+ id: LogicGate
+ name: logic gate
+ description: A logic gate with two inputs and one output. Technicians can change its mode of operation using a screwdriver.
+ components:
+ - type: Sprite
+ layers:
+ - state: base
+ - state: or
+ map: [ "enum.LogicGateLayers.Gate" ]
+ - type: LogicGate
- type: DeviceLinkSink
ports:
- - A1
- - B1
- - A2
- - B2
+ - InputA
+ - InputB
- type: DeviceLinkSource
ports:
- - O1
- - O2
+ - Output
+ - type: Construction
+ graph: LogicGate
+ node: logic_gate
+ - type: Appearance
+ - type: GenericVisualizer
+ visuals:
+ enum.LogicGateVisuals.Gate:
+ enum.LogicGateLayers.Gate:
+ Or: { state: or }
+ And: { state: and }
+ Xor: { state: xor }
+ Nor: { state: nor }
+ Nand: { state: nand }
+ Xnor: { state: xnor }
+
+- type: entity
+ parent: BaseLogicItem
+ id: EdgeDetector
+ name: edge detector
+ description: Splits rising and falling edges into unique pulses and detects how edgy you are.
+ components:
+ - type: Sprite
+ state: edge_detector
+ - type: EdgeDetector
+ - type: DeviceLinkSink
+ ports:
+ - Input
+ - type: DeviceLinkSource
+ ports:
+ - OutputHigh
+ - OutputLow
+ - type: Construction
+ graph: LogicGate
+ node: edge_detector
diff --git a/Resources/Prototypes/MachineLinking/receiver_ports.yml b/Resources/Prototypes/MachineLinking/receiver_ports.yml
index 9156fd2b59..86e61604cb 100644
--- a/Resources/Prototypes/MachineLinking/receiver_ports.yml
+++ b/Resources/Prototypes/MachineLinking/receiver_ports.yml
@@ -75,25 +75,20 @@
- type: receiverPort
id: DoorBolt
- name: "Bolt"
- description: "Bolt door when HIGH."
+ name: signal-port-name-doorbolt
+ description: signal-port-description-doorbolt
- type: receiverPort
- id: A1
- name: "Input A1"
- description: "Input A1"
+ id: InputA
+ name: signal-port-name-logic-input-a
+ description: signal-port-description-logic-input-a
- type: receiverPort
- id: B1
- name: "Input B1"
- description: "Input B1"
+ id: InputB
+ name: signal-port-name-logic-input-b
+ description: signal-port-description-logic-input-b
- type: receiverPort
- id: A2
- name: "Input A2"
- description: "Input A2"
-
-- type: receiverPort
- id: B2
- name: "Input B2"
- description: "Input B2"
+ id: Input
+ name: signal-port-name-logic-input
+ description: signal-port-description-logic-input
diff --git a/Resources/Prototypes/MachineLinking/transmitter_ports.yml b/Resources/Prototypes/MachineLinking/transmitter_ports.yml
index 41a6a80307..4949097134 100644
--- a/Resources/Prototypes/MachineLinking/transmitter_ports.yml
+++ b/Resources/Prototypes/MachineLinking/transmitter_ports.yml
@@ -34,6 +34,12 @@
description: signal-port-description-middle
defaultLinks: [ Off, Close ]
+- type: transmitterPort
+ id: DoorStatus
+ name: signal-port-name-doorstatus
+ description: signal-port-description-doorstatus
+ defaultLinks: [ DoorBolt ]
+
- type: transmitterPort
id: OrderSender
name: signal-port-name-order-sender
@@ -49,7 +55,7 @@
id: MedicalScannerSender
name: signal-port-name-med-scanner-sender
description: signal-port-description-med-scanner-sender
-
+
- type: transmitterPort
id: Timer
name: signal-port-name-timer-trigger
@@ -69,16 +75,16 @@
defaultLinks: [ ArtifactAnalyzerReceiver ]
- type: transmitterPort
- id: DoorStatus
- name: "Door Status"
- description: "HIGH when door is open, LOW when door is closed."
+ id: Output
+ name: signal-port-name-logic-output
+ description: signal-port-description-logic-output
- type: transmitterPort
- id: O1
- name: "Output 1"
- description: "Output 1"
+ id: OutputHigh
+ name: signal-port-name-logic-output-high
+ description: signal-port-description-logic-output-high
- type: transmitterPort
- id: O2
- name: "Output 2"
- description: "Output 2"
+ id: OutputLow
+ name: signal-port-name-logic-output-low
+ description: signal-port-description-logic-output-low
diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml b/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml
new file mode 100644
index 0000000000..cf620eaaca
--- /dev/null
+++ b/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml
@@ -0,0 +1,26 @@
+- type: constructionGraph
+ id: LogicGate
+ start: start
+ graph:
+ - node: start
+ edges:
+ - to: logic_gate
+ steps:
+ - material: Steel
+ amount: 3
+ doAfter: 1
+ - material: Cable
+ amount: 2
+ doAfter: 1
+ - to: edge_detector
+ steps:
+ - material: Steel
+ amount: 3
+ doAfter: 1
+ - material: Cable
+ amount: 2
+ doAfter: 1
+ - node: logic_gate
+ entity: LogicGate
+ - node: edge_detector
+ entity: EdgeDetector
diff --git a/Resources/Prototypes/Recipes/Construction/tools.yml b/Resources/Prototypes/Recipes/Construction/tools.yml
index adb6e5bdf0..cfa968b6ec 100644
--- a/Resources/Prototypes/Recipes/Construction/tools.yml
+++ b/Resources/Prototypes/Recipes/Construction/tools.yml
@@ -8,3 +8,25 @@
description: A torch fashioned from some wood.
icon: { sprite: Objects/Misc/torch.rsi, state: icon }
objectType: Item
+
+- type: construction
+ name: logic gate
+ id: LogicGate
+ graph: LogicGate
+ startNode: start
+ targetNode: logic_gate
+ category: construction-category-tools
+ description: A binary logic gate for signals.
+ icon: { sprite: Objects/Devices/gates.rsi, state: or_icon }
+ objectType: Item
+
+- type: construction
+ name: edge detector
+ id: EdgeDetector
+ graph: LogicGate
+ startNode: start
+ targetNode: edge_detector
+ category: construction-category-tools
+ description: An edge detector for signals.
+ icon: { sprite: Objects/Devices/gates.rsi, state: edge_detector }
+ objectType: Item
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/and.png b/Resources/Textures/Objects/Devices/gates.rsi/and.png
new file mode 100644
index 0000000000..426db363ca
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/and.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/base.png b/Resources/Textures/Objects/Devices/gates.rsi/base.png
new file mode 100644
index 0000000000..946bfb4a29
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/base.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/edge_detector.png b/Resources/Textures/Objects/Devices/gates.rsi/edge_detector.png
new file mode 100644
index 0000000000..9985c8b30d
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/edge_detector.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/meta.json b/Resources/Textures/Objects/Devices/gates.rsi/meta.json
index b71ae48098..1a4ca51419 100644
--- a/Resources/Textures/Objects/Devices/gates.rsi/meta.json
+++ b/Resources/Textures/Objects/Devices/gates.rsi/meta.json
@@ -1,14 +1,38 @@
{
- "version": 1,
- "license": "CC-BY-SA-3.0",
- "copyright": "Kevin Zheng 2022",
- "size": {
- "x": 32,
- "y": 32
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "or.png originally created by Kevin Zheng, 2022. All are modified by deltanedas (github) for SS14, 2023.",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "base"
},
- "states": [
- {
- "name": "or"
- }
- ]
+ {
+ "name": "or"
+ },
+ {
+ "name": "and"
+ },
+ {
+ "name": "xor"
+ },
+ {
+ "name": "nor"
+ },
+ {
+ "name": "nand"
+ },
+ {
+ "name": "xnor"
+ },
+ {
+ "name": "edge_detector"
+ },
+ {
+ "name": "or_icon"
+ }
+ ]
}
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/nand.png b/Resources/Textures/Objects/Devices/gates.rsi/nand.png
new file mode 100644
index 0000000000..3c1373f1ab
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/nand.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/nor.png b/Resources/Textures/Objects/Devices/gates.rsi/nor.png
new file mode 100644
index 0000000000..a7a6ff845d
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/nor.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/or.png b/Resources/Textures/Objects/Devices/gates.rsi/or.png
index 513d4f5aee..7a8cc0886f 100644
Binary files a/Resources/Textures/Objects/Devices/gates.rsi/or.png and b/Resources/Textures/Objects/Devices/gates.rsi/or.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/or_icon.png b/Resources/Textures/Objects/Devices/gates.rsi/or_icon.png
new file mode 100644
index 0000000000..b81b390cac
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/or_icon.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/xnor.png b/Resources/Textures/Objects/Devices/gates.rsi/xnor.png
new file mode 100644
index 0000000000..0517c09a4f
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/xnor.png differ
diff --git a/Resources/Textures/Objects/Devices/gates.rsi/xor.png b/Resources/Textures/Objects/Devices/gates.rsi/xor.png
new file mode 100644
index 0000000000..47a258c918
Binary files /dev/null and b/Resources/Textures/Objects/Devices/gates.rsi/xor.png differ
diff --git a/Resources/migration.yml b/Resources/migration.yml
index cf7b072a74..8960b593c3 100644
--- a/Resources/migration.yml
+++ b/Resources/migration.yml
@@ -53,7 +53,10 @@ FoodCondimentBottleSmallHotsauce: FoodCondimentBottleHotsauce
FoodBakedCookieFortune: FoodSnackCookieFortune
GunSafeSubMachineGunVector: GunSafeSubMachineGunDrozd
+# 2023-05-29
+OrGate: null
+
# 2023-05-31
IHSVoidsuit: null
ClothingHeadHelmetIHSVoidHelm: null
-ClothingHandsGlovesIhscombat: null
\ No newline at end of file
+ClothingHandsGlovesIhscombat: null