From 07d2430840622985e9362d6b8624b83d9ce1bcc7 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:48:42 +0000 Subject: [PATCH] logic gate stuff (#16943) Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Components/EdgeDetectorComponent.cs | 36 +++++ .../Components/LogicGateComponent.cs | 73 ++++++++++ .../Components/OrGateComponent.cs | 34 ----- .../Systems/DoorSignalControlSystem.cs | 20 +-- .../Systems/EdgeDetectorSystem.cs | 47 +++++++ .../DeviceLinking/Systems/LogicGateSystem.cs | 133 ++++++++++++++++++ .../DeviceLinking/Systems/OrGateSystem.cs | 84 ----------- .../DeviceLinking/SharedLogicGate.cs | 36 +++++ .../Locale/en-US/logic-gates/logic-gates.ftl | 3 + .../en-US/machine-linking/receiver_ports.ftl | 11 +- .../machine-linking/transmitter_ports.ftl | 11 +- .../Prototypes/DeviceLinking/sink_ports.yml | 23 ++- .../Prototypes/DeviceLinking/source_ports.yml | 20 ++- .../Prototypes/Entities/Structures/gates.yml | 69 ++++++--- .../MachineLinking/receiver_ports.yml | 27 ++-- .../MachineLinking/transmitter_ports.yml | 26 ++-- .../Construction/Graphs/tools/logic_gate.yml | 26 ++++ .../Prototypes/Recipes/Construction/tools.yml | 22 +++ .../Objects/Devices/gates.rsi/and.png | Bin 0 -> 7372 bytes .../Objects/Devices/gates.rsi/base.png | Bin 0 -> 7579 bytes .../Devices/gates.rsi/edge_detector.png | Bin 0 -> 7375 bytes .../Objects/Devices/gates.rsi/meta.json | 46 ++++-- .../Objects/Devices/gates.rsi/nand.png | Bin 0 -> 7367 bytes .../Objects/Devices/gates.rsi/nor.png | Bin 0 -> 7371 bytes .../Textures/Objects/Devices/gates.rsi/or.png | Bin 5234 -> 7365 bytes .../Objects/Devices/gates.rsi/or_icon.png | Bin 0 -> 7600 bytes .../Objects/Devices/gates.rsi/xnor.png | Bin 0 -> 7402 bytes .../Objects/Devices/gates.rsi/xor.png | Bin 0 -> 7377 bytes Resources/migration.yml | 5 +- 29 files changed, 547 insertions(+), 205 deletions(-) create mode 100644 Content.Server/DeviceLinking/Components/EdgeDetectorComponent.cs create mode 100644 Content.Server/DeviceLinking/Components/LogicGateComponent.cs delete mode 100644 Content.Server/DeviceLinking/Components/OrGateComponent.cs create mode 100644 Content.Server/DeviceLinking/Systems/EdgeDetectorSystem.cs create mode 100644 Content.Server/DeviceLinking/Systems/LogicGateSystem.cs delete mode 100644 Content.Server/DeviceLinking/Systems/OrGateSystem.cs create mode 100644 Content.Shared/DeviceLinking/SharedLogicGate.cs create mode 100644 Resources/Locale/en-US/logic-gates/logic-gates.ftl create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/and.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/base.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/edge_detector.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/nand.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/nor.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/or_icon.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/xnor.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/xor.png 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 0000000000000000000000000000000000000000..426db363ca3571f634c6dea21caeef349a6e7c2c GIT binary patch literal 7372 zcmd5>32YTr6rCctJc~kIzzvI{qJ%p0-psriH`0Z$xv&_m6r}&p5=vOAty-%@5Q^Z2 zl_G)-M6{wJXn=~Wrh*tl6rvaFH4i5T_XnLqvWet(xwo7S29nR&}S_uTXD zyVFb;jJ|VNPjV@dB&p|!;dx`>o{7J+&xC79d3l2*rA;r&%^h82S&}rc@}bIYqjJXd z-Sxzju~k2eF3g)dXJ~Hrg?ZNg!&mipU-)VzAJT0|*2x?CugM-YXK%0C2Cd=BeV1;y zxO;Z*ybZM{Yv&#~_}c7@@~mUqhb%8yzU1U>eSWHY_9g9GxBn}H`qs}nwB_=NzrFuq zy*#jmvsOmd-ouX5YJY z(1BGo*71)%`N*1=KJf9^8p@uVbkV9Qf4}w3{JF>K)|Qu#>v{Q%!okz*ieVKq_uoF| z)GNbgE*Ml6eY0xt@nbvJ-LUF#*}T;g zzFV=R+TEUa?s@fQvh9(*o5t_{ z@kI6dVe_YcdgJDbIk|l+PyJFmDQ)v)vS5=qLs8h>eJHA5bjq;9=R zhvt`#jXaWB{NN0?XsVN0`e?C}>68_@62U9U#Dl7os2VqPQ`coUR8uu+1Re}*!4dVY zimbw2@t$Ss->@z8lg0rG0}N*24-PXJaG)Qal^Bp2bD{+EWBmlCd502#Ck-BB?Fcf% zW>oBt#vD)s`q614P>caXsyN9)bBHPi#LzP`ip{e;5oB364IU1e*f9g&9Ii+`_EQ4JxY~^t3Sm*Pn}-br=wWD`ON+=z%i=eR!n| z`-*Yk|_sm6eX?@`LEQ!)y@GjYgv`rOFxC0<#U_>c%nIlJ-#X)7*9bl;2=u8Lkv8`LW!x@ed^Z;x?BILm${y^h& zk4YQ`Afg^80}}&Mgn_hRM`glP&=r3&wf#NGbw~z^Oh9!Drm4ElA{tdyRZrmIk0BL< za7ZRDvj}ym%e{j-9N1FF^TnwPxn$xAPnd5mq!FdI?*Q(MgO*$+kkbaWF(4fGd4!n& z4bT>pqmc13gl2*W4ls)`1z9M#)PNDj3`+um_@^Ku8p7czX@uE(uAR8L1VRA8bQ=PT z8DTJZCdL5L#fAEPy~a%p-60T-^e;v9;5H8CEZJM<8PwB@LOG$qdl| zs}|!-Wl;JcBpEVHK?Ha);2DQdP0&DrEvsp`Z6RHC!vjE%Nn8f=p^B&|priBFOf;h< zmLwU?CMrrzqJq_whsT0Zg4GWUP3C}j7Sw>C&Uk(C4HOtOH=p<* zZ8u1qA2|0IsR@L=!BG6Ig^%+|5K8ir>Z=iWjlTr2-|KCBO_AWmBMRu}b}HA{rcK2p z`P>cx!lmjEAmrH9K;gXeHS20dO;@hXjG6``g0&e)G;7-G44~G65m|KxBhh1PErC&L z!HCcTkT{fDtAl_V|A8JsDKJV5MubWMNHk1rbq-L=!HAqX zhmq*B;)M|xMOQzyj6QH&S(+ZF{-1b%zMr}70X9Cda2Gayc>^R+4yFv@c z(k`Gd`>p~C2NUMgj=zsYn9c-hJ{XZxSBWjM2)B|jYHKiI7*A~crjX<-JAQZbJ`8g1 ze;_A|Cd)6tzY~^@$jiPvf5*=+)%Bj0ncjW&h{)(ma-F?PN2ZmV*QVdL?#h0<>>d~N ztb3q($IgxC*531F&Km8PGe^yScfz`Y?ze85{Nj=7B~LF@3ZI?4Xz|#r3%`79;XAL( zb$6u={B-1i(uYfCW~}a^-CUEAU$J<2y_{8;B~QwK@-yDNV&i!$ v7awuEEnB+%j#>XaUqJHztumhfebn?r*YO{B*ynEnlG2Ewcjm3lDSYxjJotPT literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..946bfb4a29157d41a0cfb1c9a536a9f3d90945e9 GIT binary patch literal 7579 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9985c8b30defd4b05d252f6ba14cf89d3cce9450 GIT binary patch literal 7375 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a7a6ff845d71060657afdca6d71447df48e626fa GIT binary patch literal 7371 zcmd5>3v3is6rCyveW6TGk`)%2_U3_qESJV-I<+G`BjRtRE;Q< zPt-`IK!J)8MAQmVp?*M75H*GwFPhi%UvsB`Ix2;jm$23r$m!dY3&|_WtPH zaoL|dc7H+nPh%(NEto%iSk7g6=KjMyd)Q0AS;vQ5Fl4})zCAYOjGq5xW_7Jvd+omN zTd(Ml(=~5v^_l7g2W!?p-mPT7u^mI!7O!1-=GLrVc0RR2Jz)1((=YqTyhGcsp7i@$ z9~`k3Uo$uwUj546(%hU0n@@F`_G-;DRrhW>rB0f3GJX7o6BnJ{`qEV$L#q}ZwsY=$ zuiwFq8_bitKHX(5?9%&@^|jHb@4I~C{pVi!Zt;R+J2#h& z366X6Yqf{krzCf6m+P0G{de%^KklA$&R+QEEiae%KY48Tn|(JPjxMaa z=lkbZR@yuAZvIw1)b*XWkNmK0^T%h7%7faHBUieFkZW!{ABwdg> zd-z?m3qo@OMYCqwh0`K|*>j5`fk?E_mIz)+01v8AqNrTg3{8{aPz=SOA$ZWS1xMJq z24w~ALFZYf?hV^QKdBp_Fu-6I{@^gYfe7@&b1(*E#GC}Z`LTWi)0`t1f+rOoW9<+! z!)93Q562u(9s1E}9Z-w`L#jB*M01EL2E@=aGK$SJ9T8+%Gj#3`nOHFc*Bq`$J@!)= zr<$q-xdK;(%A9KkJnK|eIOu6%0JcdDv?Z!5XkCZb_NG#qQ5s=D2(FA#IH3p55cJ`d zGLVY}3DcpW#s&}?V+O(e@q0SpFdpQFb-S;Dgjtj{Hwuepn-amRn;QW|C#tOJnhnv| zoH-D6RqSK2c0vCr6 zH8>;8WHyJz(SjP&!kWrKa1G@L(X|M(Ks-Af4%?JMzo9AEqjIJy7LyeV7L~zVNOz*> zJcR3}G9FSQs-nRi02u=#N}0_fa)_B6REFIFhMI-WG!P%#nyE!N!!d#$fDK55JXpvb zsGIIEiNgRy*x{sOVnB*8kQVHyOqc??;!mcQyC=B@$v}|_sBXeEMblVFrHZ0x2|U~} zq@oiJ$;4$AqBga;bI=F}wlw1S;?#y*GVp{a%r_g-h*HaS0C&bgORf;eX`Naa5RSV% z{7irbXbZ|w$aon-GeCq0FpDt-Stz(vff2?GO9FworywF4!r>^X``J6LBXM;JgaCqR z76cYE!eAWDK)MsWS zGeiTdT8uM=LFt2#WXP}}BEX9Q&p3o)fCfR>vZ{*P7SdJI9RT#0#HBYMs)&LDMl{}( ziK;iok|d*9k0owQ!(A(T`d(It3N&;bL&=-GG;p2Sbg_69ax@ve{<1Yd1cY5nyQ^b4mhyuE~oys+~ zX;U#tKDSkXaH(1a2sySlkU#HS&Dxt${grDrqx!*!V9f>+&FZ(>0;s8AL{@FVNc7lL zOJI~*Fe0=7Bo3w4Y9*k$KO$1|o@{n2F={~>NnDurXOyT@3XBqi5us855)BhuZ3EPJ zFe0b6VI=x&yp_Nxm0(2R1R!xRl~(HkoqsSQ-_~O!;-=CPWBETY64|H3NDQV0K%%MG zN{NwZDz=&cYAl$PfW*m-wVGhmn5j5c52*RUGyxdc?Zh!SASGI^k?Z% x&Woy?_T8%EGo;glW~5L4r%!rm?g#ra=A{okQ91C^#lTS-H9S9WbME9P{sY6^eF^{o literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/gates.rsi/or.png b/Resources/Textures/Objects/Devices/gates.rsi/or.png index 513d4f5aeea8e6d672346b5852430e945b9b2c30..7a8cc0886f768275720cc0a50c1d5ab0ee47d2ca 100644 GIT binary patch literal 7365 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 R@P8dHJtZ@_ZrZ}i{{aoi>RkW; literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0517c09a4f204df94f416664b8638fa6c4ad7323 GIT binary patch literal 7402 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 DOudE( literal 0 HcmV?d00001 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