A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3
4namespace Unity.VisualScripting
5{
6 /// <summary>
7 /// Loops between a first and last index at a specified step.
8 /// </summary>
9 [UnitTitle("For Loop")]
10 [UnitCategory("Control")]
11 [UnitOrder(9)]
12 public sealed class For : LoopUnit
13 {
14 /// <summary>
15 /// The index at which to start the loop (inclusive).
16 /// </summary>
17 [PortLabel("First")]
18 [DoNotSerialize]
19 public ValueInput firstIndex { get; private set; }
20
21 /// <summary>
22 /// The index at which to end the loop (exclusive).
23 /// </summary>
24 [PortLabel("Last")]
25 [DoNotSerialize]
26 public ValueInput lastIndex { get; private set; }
27
28 /// <summary>
29 /// The value by which the index will be incremented (or decremented, if negative) after each loop.
30 /// </summary>
31 [DoNotSerialize]
32 public ValueInput step { get; private set; }
33
34 /// <summary>
35 /// The current index of the loop.
36 /// </summary>
37 [PortLabel("Index")]
38 [DoNotSerialize]
39 public ValueOutput currentIndex { get; private set; }
40
41 protected override void Definition()
42 {
43 firstIndex = ValueInput(nameof(firstIndex), 0);
44 lastIndex = ValueInput(nameof(lastIndex), 10);
45 step = ValueInput(nameof(step), 1);
46 currentIndex = ValueOutput<int>(nameof(currentIndex));
47 base.Definition();
48
49 Requirement(firstIndex, enter);
50 Requirement(lastIndex, enter);
51 Requirement(step, enter);
52 Assignment(enter, currentIndex);
53 }
54
55 private int Start(Flow flow, out int currentIndex, out int lastIndex, out bool ascending)
56 {
57 var firstIndex = flow.GetValue<int>(this.firstIndex);
58 lastIndex = flow.GetValue<int>(this.lastIndex);
59 ascending = firstIndex <= lastIndex;
60 currentIndex = firstIndex;
61 flow.SetValue(this.currentIndex, currentIndex);
62
63 return flow.EnterLoop();
64 }
65
66 private bool CanMoveNext(int currentIndex, int lastIndex, bool ascending)
67 {
68 if (ascending)
69 {
70 return currentIndex < lastIndex;
71 }
72 else
73 {
74 return currentIndex > lastIndex;
75 }
76 }
77
78 private void MoveNext(Flow flow, ref int currentIndex)
79 {
80 currentIndex += flow.GetValue<int>(step);
81 flow.SetValue(this.currentIndex, currentIndex);
82 }
83
84 protected override ControlOutput Loop(Flow flow)
85 {
86 var loop = Start(flow, out int currentIndex, out int lastIndex, out bool ascending);
87
88 if (!IsStepValueZero())
89 {
90 var stack = flow.PreserveStack();
91
92 while (flow.LoopIsNotBroken(loop) && CanMoveNext(currentIndex, lastIndex, ascending))
93 {
94 flow.Invoke(body);
95
96 flow.RestoreStack(stack);
97
98 MoveNext(flow, ref currentIndex);
99 }
100
101 flow.DisposePreservedStack(stack);
102 }
103
104 flow.ExitLoop(loop);
105
106 return exit;
107 }
108
109 protected override IEnumerator LoopCoroutine(Flow flow)
110 {
111 var loop = Start(flow, out int currentIndex, out int lastIndex, out bool ascending);
112
113 var stack = flow.PreserveStack();
114
115 while (flow.LoopIsNotBroken(loop) && CanMoveNext(currentIndex, lastIndex, ascending))
116 {
117 yield return body;
118
119 flow.RestoreStack(stack);
120
121 MoveNext(flow, ref currentIndex);
122 }
123
124 flow.DisposePreservedStack(stack);
125
126 flow.ExitLoop(loop);
127
128 yield return exit;
129 }
130
131 public bool IsStepValueZero()
132 {
133 var isDefaultZero = !step.hasValidConnection && (int)defaultValues[step.key] == 0;
134 var isConnectedToLiteralZero = false;
135
136 if (step.hasValidConnection && step.connection.source.unit is Literal literal)
137 {
138 if (Convert.ToInt32(literal.value) == 0)
139 {
140 isConnectedToLiteralZero = true;
141 }
142 }
143
144 return isDefaultZero || isConnectedToLiteralZero;
145 }
146 }
147}