A game about forced loneliness, made by TACStudios
1using System.Collections; 2using System.Collections.Generic; 3using System.Collections.ObjectModel; 4using UnityEngine; 5 6namespace Unity.VisualScripting 7{ 8 /// <summary> 9 /// Delays flow by waiting until multiple input flows have been executed. 10 /// </summary> 11 [UnitCategory("Time")] 12 [UnitOrder(6)] 13 [TypeIcon(typeof(WaitUnit))] 14 public sealed class WaitForFlow : Unit, IGraphElementWithData 15 { 16 public sealed class Data : IGraphElementData 17 { 18 public bool[] inputsActivated; 19 public bool isWaitingCoroutine; 20 } 21 22 /// <summary> 23 /// Whether the activation status should be reset on exit. 24 /// </summary> 25 [Serialize] 26 [Inspectable] 27 public bool resetOnExit { get; set; } 28 29 [SerializeAs(nameof(inputCount))] 30 private int _inputCount = 2; 31 32 [DoNotSerialize] 33 [Inspectable, UnitHeaderInspectable("Inputs")] 34 public int inputCount 35 { 36 get => _inputCount; 37 set => _inputCount = Mathf.Clamp(value, 2, 10); 38 } 39 40 [DoNotSerialize] 41 public ReadOnlyCollection<ControlInput> awaitedInputs { get; private set; } 42 43 /// <summary> 44 /// Trigger to reset the activation status. 45 /// </summary> 46 [DoNotSerialize] 47 public ControlInput reset { get; private set; } 48 49 /// <summary> 50 /// Triggered after all inputs have been entered at least once. 51 /// </summary> 52 [DoNotSerialize] 53 [PortLabelHidden] 54 public ControlOutput exit { get; private set; } 55 56 protected override void Definition() 57 { 58 var _awaitedInputs = new List<ControlInput>(); 59 60 awaitedInputs = _awaitedInputs.AsReadOnly(); 61 62 exit = ControlOutput(nameof(exit)); 63 64 for (var i = 0; i < inputCount; i++) 65 { 66 var _i = i; // Cache outside closure 67 68 var awaitedInput = ControlInputCoroutine(_i.ToString(), (flow) => Enter(flow, _i), (flow) => EnterCoroutine(flow, _i)); 69 70 _awaitedInputs.Add(awaitedInput); 71 72 Succession(awaitedInput, exit); 73 } 74 75 reset = ControlInput(nameof(reset), Reset); 76 } 77 78 public IGraphElementData CreateData() 79 { 80 return new Data() { inputsActivated = new bool[inputCount] }; 81 } 82 83 private ControlOutput Enter(Flow flow, int index) 84 { 85 var data = flow.stack.GetElementData<Data>(this); 86 87 data.inputsActivated[index] = true; 88 89 if (CheckActivated(flow)) 90 { 91 if (resetOnExit) 92 { 93 Reset(flow); 94 } 95 96 return exit; 97 } 98 else 99 { 100 return null; 101 } 102 } 103 104 private bool CheckActivated(Flow flow) 105 { 106 var data = flow.stack.GetElementData<Data>(this); 107 108 for (int i = 0; i < data.inputsActivated.Length; i++) 109 { 110 if (!data.inputsActivated[i]) 111 { 112 return false; 113 } 114 } 115 116 return true; 117 } 118 119 private IEnumerator EnterCoroutine(Flow flow, int index) 120 { 121 var data = flow.stack.GetElementData<Data>(this); 122 123 data.inputsActivated[index] = true; 124 125 if (data.isWaitingCoroutine) 126 { 127 // Another input started an async wait, 128 // we'll let that flow be responsible for 129 // triggering the exit. 130 yield break; 131 } 132 133 if (!CheckActivated(flow)) 134 { 135 data.isWaitingCoroutine = true; 136 137 yield return new WaitUntil(() => CheckActivated(flow)); 138 139 data.isWaitingCoroutine = false; 140 } 141 142 if (resetOnExit) 143 { 144 Reset(flow); 145 } 146 147 yield return exit; 148 } 149 150 private ControlOutput Reset(Flow flow) 151 { 152 var data = flow.stack.GetElementData<Data>(this); 153 154 for (int i = 0; i < data.inputsActivated.Length; i++) 155 { 156 data.inputsActivated[i] = false; 157 } 158 159 return null; 160 } 161 } 162}