LogicGateSystem.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using Content.Server.DeviceLinking.Components;
  2. using Content.Server.DeviceNetwork;
  3. using Content.Shared.DeviceLinking;
  4. using Content.Shared.Examine;
  5. using Content.Shared.Interaction;
  6. using Content.Shared.Popups;
  7. using Content.Shared.Timing;
  8. using Content.Shared.Tools.Systems;
  9. using Robust.Shared.Audio.Systems;
  10. using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
  11. namespace Content.Server.DeviceLinking.Systems;
  12. public sealed class LogicGateSystem : EntitySystem
  13. {
  14. [Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
  15. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  16. [Dependency] private readonly SharedAudioSystem _audio = default!;
  17. [Dependency] private readonly SharedPopupSystem _popup = default!;
  18. [Dependency] private readonly SharedToolSystem _tool = default!;
  19. [Dependency] private readonly UseDelaySystem _useDelay = default!;
  20. private readonly int GateCount = Enum.GetValues(typeof(LogicGate)).Length;
  21. public override void Initialize()
  22. {
  23. base.Initialize();
  24. SubscribeLocalEvent<LogicGateComponent, ComponentInit>(OnInit);
  25. SubscribeLocalEvent<LogicGateComponent, ExaminedEvent>(OnExamined);
  26. SubscribeLocalEvent<LogicGateComponent, InteractUsingEvent>(OnInteractUsing);
  27. SubscribeLocalEvent<LogicGateComponent, SignalReceivedEvent>(OnSignalReceived);
  28. }
  29. public override void Update(float deltaTime)
  30. {
  31. var query = EntityQueryEnumerator<LogicGateComponent>();
  32. while (query.MoveNext(out var uid, out var comp))
  33. {
  34. // handle momentary pulses - high when received then low the next tick
  35. if (comp.StateA == SignalState.Momentary)
  36. {
  37. comp.StateA = SignalState.Low;
  38. }
  39. if (comp.StateB == SignalState.Momentary)
  40. {
  41. comp.StateB = SignalState.Low;
  42. }
  43. // output most likely changed so update it
  44. UpdateOutput(uid, comp);
  45. }
  46. }
  47. private void OnInit(EntityUid uid, LogicGateComponent comp, ComponentInit args)
  48. {
  49. _deviceLink.EnsureSinkPorts(uid, comp.InputPortA, comp.InputPortB);
  50. _deviceLink.EnsureSourcePorts(uid, comp.OutputPort);
  51. }
  52. private void OnExamined(EntityUid uid, LogicGateComponent comp, ExaminedEvent args)
  53. {
  54. if (!args.IsInDetailsRange)
  55. return;
  56. args.PushMarkup(Loc.GetString("logic-gate-examine", ("gate", comp.Gate.ToString().ToUpper())));
  57. }
  58. private void OnInteractUsing(EntityUid uid, LogicGateComponent comp, InteractUsingEvent args)
  59. {
  60. if (args.Handled || !_tool.HasQuality(args.Used, comp.CycleQuality))
  61. return;
  62. // no sound spamming
  63. if (TryComp<UseDelayComponent>(uid, out var useDelay)
  64. && !_useDelay.TryResetDelay((uid, useDelay), true))
  65. return;
  66. // cycle through possible gates
  67. var gate = (int) comp.Gate;
  68. gate = ++gate % GateCount;
  69. comp.Gate = (LogicGate) gate;
  70. // since gate changed the output probably has too, update it
  71. UpdateOutput(uid, comp);
  72. // notify the user
  73. _audio.PlayPvs(comp.CycleSound, uid);
  74. var msg = Loc.GetString("logic-gate-cycle", ("gate", comp.Gate.ToString().ToUpper()));
  75. _popup.PopupEntity(msg, uid, args.User);
  76. _appearance.SetData(uid, LogicGateVisuals.Gate, comp.Gate);
  77. }
  78. private void OnSignalReceived(EntityUid uid, LogicGateComponent comp, ref SignalReceivedEvent args)
  79. {
  80. // default to momentary for compatibility with non-logic signals.
  81. // currently only door status and logic gates have logic signal state.
  82. var state = SignalState.Momentary;
  83. args.Data?.TryGetValue(DeviceNetworkConstants.LogicState, out state);
  84. // update the state for the correct port
  85. if (args.Port == comp.InputPortA)
  86. {
  87. comp.StateA = state;
  88. _appearance.SetData(uid, LogicGateVisuals.InputA, state == SignalState.High); //If A == High => Sets input A sprite to True
  89. }
  90. else if (args.Port == comp.InputPortB)
  91. {
  92. comp.StateB = state;
  93. _appearance.SetData(uid, LogicGateVisuals.InputB, state == SignalState.High); //If B == High => Sets input B sprite to True
  94. }
  95. UpdateOutput(uid, comp);
  96. }
  97. /// <summary>
  98. /// Handle the logic for a logic gate, invoking the port if the output changed.
  99. /// </summary>
  100. private void UpdateOutput(EntityUid uid, LogicGateComponent comp)
  101. {
  102. // get the new output value now that it's changed
  103. // momentary is treated as high for the current tick, after updating it will be reset to low
  104. var a = comp.StateA != SignalState.Low;
  105. var b = comp.StateB != SignalState.Low;
  106. var output = false;
  107. switch (comp.Gate)
  108. {
  109. case LogicGate.Or:
  110. output = a || b;
  111. break;
  112. case LogicGate.And:
  113. output = a && b;
  114. break;
  115. case LogicGate.Xor:
  116. output = a != b;
  117. break;
  118. case LogicGate.Nor:
  119. output = !(a || b);
  120. break;
  121. case LogicGate.Nand:
  122. output = !(a && b);
  123. break;
  124. case LogicGate.Xnor:
  125. output = a == b;
  126. break;
  127. }
  128. _appearance.SetData(uid, LogicGateVisuals.Output, output);
  129. // only send a payload if it actually changed
  130. if (output != comp.LastOutput)
  131. {
  132. comp.LastOutput = output;
  133. _deviceLink.SendSignal(uid, comp.OutputPort, output);
  134. }
  135. }
  136. }