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}