--- /dev/null
+using Content.Shared.DeviceLinking.Systems;
+
+namespace Content.Client.DeviceLinking.Systems;
+
+public sealed class RandomGateSystem : SharedRandomGateSystem;
--- /dev/null
+using Content.Shared.DeviceLinking;
+using JetBrains.Annotations;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.DeviceLinking.UI;
+
+[UsedImplicitly]
+public sealed class RandomGateBoundUserInterface : BoundUserInterface
+{
+ private RandomGateSetupWindow? _window;
+
+ public RandomGateBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
+
+ protected override void Open()
+ {
+ base.Open();
+ _window = this.CreateWindow<RandomGateSetupWindow>();
+ _window.OnApplyPressed += OnProbabilityChanged;
+ }
+
+ private void OnProbabilityChanged(string value)
+ {
+ if (!float.TryParse(value, out var probability))
+ return;
+
+ SendPredictedMessage(new RandomGateProbabilityChangedMessage(probability));
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+ if (state is not RandomGateBoundUserInterfaceState castState || _window == null)
+ return;
+
+ _window.SetProbability(castState.SuccessProbability * 100);
+ }
+}
--- /dev/null
+<controls:FancyWindow xmlns="https://spacestation14.io"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
+ Title="{Loc 'random-gate-menu-setup'}"
+ MinSize="260 115">
+ <BoxContainer Orientation="Vertical" Margin="5" SeparationOverride="10">
+ <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
+ <Label Text="{Loc 'random-gate-menu-settings'}" VerticalAlignment="Center" />
+ <Control HorizontalExpand="True" />
+ <LineEdit Name="ProbabilityInput" MinSize="70 0" />
+ </BoxContainer>
+ <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
+ <Control HorizontalExpand="True" />
+ <Button Name="ApplyButton"
+ Text="{Loc 'random-gate-menu-apply'}"
+ HorizontalAlignment="Right" />
+ </BoxContainer>
+ </BoxContainer>
+</controls:FancyWindow>
--- /dev/null
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.DeviceLinking.UI;
+
+/// <summary>
+/// Window for setting up the random gate probability.
+/// </summary>
+[GenerateTypedNameReferences]
+public sealed partial class RandomGateSetupWindow : FancyWindow
+{
+ /// <summary>
+ /// Event triggered when the "Apply" button is pressed.
+ /// </summary>
+ public event Action<string>? OnApplyPressed;
+
+ public RandomGateSetupWindow()
+ {
+ RobustXamlLoader.Load(this);
+ ApplyButton.OnPressed += _ => OnApplyPressed?.Invoke(ProbabilityInput.Text);
+ }
+
+ public void SetProbability(float probability)
+ {
+ ProbabilityInput.Text = probability.ToString("0.00");
+ }
+}
--- /dev/null
+using Content.Shared.DeviceLinking.Components;
+using Content.Shared.DeviceLinking.Events;
+using Content.Shared.DeviceLinking.Systems;
+using Robust.Shared.Random;
+
+namespace Content.Server.DeviceLinking.Systems;
+
+public sealed class RandomGateSystem : SharedRandomGateSystem
+{
+ [Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<RandomGateComponent, SignalReceivedEvent>(OnSignalReceived);
+ }
+
+ private void OnSignalReceived(Entity<RandomGateComponent> ent, ref SignalReceivedEvent args)
+ {
+ if (args.Port != ent.Comp.InputPort)
+ return;
+
+ var output = _random.Prob(ent.Comp.SuccessProbability);
+ if (output != ent.Comp.LastOutput)
+ {
+ ent.Comp.LastOutput = output;
+ Dirty(ent);
+ _deviceLink.SendSignal(ent.Owner, ent.Comp.OutputPort, output);
+ }
+ }
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DeviceLinking.Components;
+
+/// <summary>
+/// A component for a random gate, which outputs a signal with a given probability.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class RandomGateComponent : Component
+{
+ /// <summary>
+ /// The input port for receiving signals.
+ /// </summary>
+ [DataField]
+ public ProtoId<SinkPortPrototype> InputPort = "RandomGateInput";
+
+ /// <summary>
+ /// The output port for sending signals.
+ /// </summary>
+ [DataField]
+ public ProtoId<SourcePortPrototype> OutputPort = "Output";
+
+ /// <summary>
+ /// The last output state of the gate.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool LastOutput;
+
+ /// <summary>
+ /// The probability (0.0 to 1.0) that the gate will output a signal.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public float SuccessProbability = 0.5f;
+}
--- /dev/null
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.DeviceLinking;
+
+[Serializable, NetSerializable]
+public sealed class RandomGateBoundUserInterfaceState(float successProbability) : BoundUserInterfaceState
+{
+ public float SuccessProbability = successProbability;
+}
+
+[Serializable, NetSerializable]
+public sealed class RandomGateProbabilityChangedMessage(float probability) : BoundUserInterfaceMessage
+{
+ public float Probability = probability;
+}
+
+[Serializable, NetSerializable]
+public enum RandomGateUiKey : byte
+{
+ Key
+}
--- /dev/null
+using Content.Shared.DeviceLinking.Components;
+using Content.Shared.UserInterface;
+
+namespace Content.Shared.DeviceLinking.Systems;
+
+public abstract class SharedRandomGateSystem : EntitySystem
+{
+ [Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<RandomGateComponent, AfterActivatableUIOpenEvent>(OnAfterActivatableUIOpen);
+ SubscribeLocalEvent<RandomGateComponent, RandomGateProbabilityChangedMessage>(OnProbabilityChanged);
+ }
+
+ private void OnAfterActivatableUIOpen(Entity<RandomGateComponent> ent, ref AfterActivatableUIOpenEvent args)
+ {
+ UpdateUI(ent);
+ }
+
+ private void OnProbabilityChanged(Entity<RandomGateComponent> ent, ref RandomGateProbabilityChangedMessage args)
+ {
+ ent.Comp.SuccessProbability = Math.Clamp(args.Probability, 0f, 100f) / 100f;
+ Dirty(ent);
+ UpdateUI(ent);
+ }
+
+ private void UpdateUI(Entity<RandomGateComponent> ent)
+ {
+ if (!_ui.HasUi(ent.Owner, RandomGateUiKey.Key))
+ return;
+
+ _ui.SetUiState(ent.Owner, RandomGateUiKey.Key, new RandomGateBoundUserInterfaceState(ent.Comp.SuccessProbability));
+ }
+}
--- /dev/null
+random-gate-menu-settings = Success Probability (%):
+random-gate-menu-setup = Random Gate Setup
+random-gate-menu-apply = Apply
signal-port-name-logic-enable = Enable
signal-port-description-logic-enable = Only loads the input signal into the memory cell when HIGH.
+
+signal-port-name-logic-random-input = Input Signal
+signal-port-description-logic-random-input = Receives any signal to trigger a random output.
name: signal-port-name-logic-enable
description: signal-port-description-logic-enable
+- type: sinkPort
+ id: RandomGateInput
+ name: signal-port-name-logic-random-input
+ description: signal-port-description-logic-random-input
+
- type: sinkPort
id: SetParticleDelta
name: signal-port-name-set-particle-delta
- Output
- type: Construction
node: memory_cell
+
+- type: entity
+ parent: BaseLogicItem
+ id: RandomGate
+ name: random gate
+ description: A logic gate that outputs a random signal when input changes.
+ components:
+ - type: Sprite
+ layers:
+ - state: base
+ - state: random_gate
+ - type: RandomGate
+ - type: DeviceLinkSink
+ ports:
+ - RandomGateInput
+ - type: DeviceLinkSource
+ ports:
+ - Output
+ - type: UserInterface
+ interfaces:
+ enum.RandomGateUiKey.Key:
+ type: RandomGateBoundUserInterface
+ - type: ActivatableUI
+ key: enum.RandomGateUiKey.Key
+ - type: Construction
+ node: random_gate
- material: Cable
amount: 2
doAfter: 1
+ - to: random_gate
+ steps:
+ - material: MetalRod
+ amount: 1
+ doAfter: 1
+ - material: Cable
+ amount: 1
+ doAfter: 1
- node: logic_gate
entity: LogicGateOr
edges:
- !type:SpawnPrototype
prototype: CableApcStack1
amount: 2
+ - node: random_gate
+ entity: RandomGate
+ edges:
+ - to: empty
+ steps:
+ - tool: Prying
+ doAfter: 2
+ completed:
+ - !type:SpawnPrototype
+ prototype: CableApcStack1
+ amount: 1
+ - !type:SpawnPrototype
+ prototype: PartRodMetal1
+ amount: 1
targetNode: memory_cell
category: construction-category-tools
objectType: Item
+
+- type: construction
+ id: RandomGate
+ graph: LogicGate
+ startNode: start
+ targetNode: random_gate
+ category: construction-category-tools
+ objectType: Item
{
"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, 2024. Sprites logic_a logic_b logic_o were made by 0tito (github) for SS14",
+ "copyright": "or.png originally created by Kevin Zheng, 2022. All are modified by deltanedas (github) for SS14, 2024. Sprites logic_a logic_b logic_o were made by 0tito (github) for SS14. 2025, added random_gate.png by borsh.",
"size": {
"x": 32,
"y": 32
},
{
"name": "memory_cell"
+ },
+ {
+ "name": "random_gate"
}
]
}