A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3
4namespace Unity.VisualScripting
5{
6 /// <summary>
7 /// Loops over each element of a collection.
8 /// </summary>
9 [UnitTitle("For Each Loop")]
10 [UnitCategory("Control")]
11 [UnitOrder(10)]
12 public class ForEach : LoopUnit
13 {
14 /// <summary>
15 /// The collection over which to loop.
16 /// </summary>
17 [DoNotSerialize]
18 [PortLabelHidden]
19 public ValueInput collection { get; private set; }
20
21 /// <summary>
22 /// The current index of the loop.
23 /// </summary>
24 [DoNotSerialize]
25 [PortLabel("Index")]
26 public ValueOutput currentIndex { get; private set; }
27
28 /// <summary>
29 /// The key of the current item of the loop.
30 /// </summary>
31 [DoNotSerialize]
32 [PortLabel("Key")]
33 public ValueOutput currentKey { get; private set; }
34
35 /// <summary>
36 /// The current item of the loop.
37 /// </summary>
38 [DoNotSerialize]
39 [PortLabel("Item")]
40 public ValueOutput currentItem { get; private set; }
41
42 [Serialize]
43 [Inspectable, UnitHeaderInspectable("Dictionary")]
44 [InspectorToggleLeft]
45 public bool dictionary { get; set; }
46
47 protected override void Definition()
48 {
49 base.Definition();
50
51 if (dictionary)
52 {
53 collection = ValueInput<IDictionary>(nameof(collection));
54 }
55 else
56 {
57 collection = ValueInput<IEnumerable>(nameof(collection));
58 }
59
60 currentIndex = ValueOutput<int>(nameof(currentIndex));
61
62 if (dictionary)
63 {
64 currentKey = ValueOutput<object>(nameof(currentKey));
65 }
66
67 currentItem = ValueOutput<object>(nameof(currentItem));
68
69 Requirement(collection, enter);
70 Assignment(enter, currentIndex);
71 Assignment(enter, currentItem);
72
73 if (dictionary)
74 {
75 Assignment(enter, currentKey);
76 }
77 }
78
79 private int Start(Flow flow, out IEnumerator enumerator, out IDictionaryEnumerator dictionaryEnumerator, out int currentIndex)
80 {
81 if (dictionary)
82 {
83 dictionaryEnumerator = flow.GetValue<IDictionary>(collection).GetEnumerator();
84 enumerator = dictionaryEnumerator;
85 }
86 else
87 {
88 enumerator = flow.GetValue<IEnumerable>(collection).GetEnumerator();
89 dictionaryEnumerator = null;
90 }
91
92 currentIndex = -1;
93
94 return flow.EnterLoop();
95 }
96
97 private bool MoveNext(Flow flow, IEnumerator enumerator, IDictionaryEnumerator dictionaryEnumerator, ref int currentIndex)
98 {
99 var result = enumerator.MoveNext();
100
101 if (result)
102 {
103 if (dictionary)
104 {
105 flow.SetValue(currentKey, dictionaryEnumerator.Key);
106 flow.SetValue(currentItem, dictionaryEnumerator.Value);
107 }
108 else
109 {
110 flow.SetValue(currentItem, enumerator.Current);
111 }
112
113 currentIndex++;
114
115 flow.SetValue(this.currentIndex, currentIndex);
116 }
117
118 return result;
119 }
120
121 protected override ControlOutput Loop(Flow flow)
122 {
123 var loop = Start(flow, out var enumerator, out var dictionaryEnumerator, out var currentIndex);
124
125 var stack = flow.PreserveStack();
126
127 try
128 {
129 while (flow.LoopIsNotBroken(loop) && MoveNext(flow, enumerator, dictionaryEnumerator, ref currentIndex))
130 {
131 flow.Invoke(body);
132
133 flow.RestoreStack(stack);
134 }
135 }
136 finally
137 {
138 (enumerator as IDisposable)?.Dispose();
139 }
140
141 flow.DisposePreservedStack(stack);
142
143 flow.ExitLoop(loop);
144
145 return exit;
146 }
147
148 protected override IEnumerator LoopCoroutine(Flow flow)
149 {
150 var loop = Start(flow, out var enumerator, out var dictionaryEnumerator, out var currentIndex);
151
152 var stack = flow.PreserveStack();
153
154 try
155 {
156 while (flow.LoopIsNotBroken(loop) && MoveNext(flow, enumerator, dictionaryEnumerator, ref currentIndex))
157 {
158 yield return body;
159
160 flow.RestoreStack(stack);
161 }
162 }
163 finally
164 {
165 (enumerator as IDisposable)?.Dispose();
166 }
167
168 flow.DisposePreservedStack(stack);
169
170 flow.ExitLoop(loop);
171
172 yield return exit;
173 }
174 }
175}