A game about forced loneliness, made by TACStudios
1using System;
2using System.ComponentModel;
3
4namespace Unity.VisualScripting
5{
6 [TypeIcon(typeof(FlowGraph))]
7 [UnitCategory("Nesting")]
8 [UnitTitle("Subgraph")]
9 [RenamedFrom("Bolt.SuperUnit")]
10 [RenamedFrom("Unity.VisualScripting.SuperUnit")]
11 [DisplayName("Subgraph Node")]
12 public sealed class SubgraphUnit : NesterUnit<FlowGraph, ScriptGraphAsset>, IGraphEventListener, IGraphElementWithData
13 {
14 public sealed class Data : IGraphElementData
15 {
16 public bool isListening;
17 }
18
19 public IGraphElementData CreateData()
20 {
21 return new Data();
22 }
23
24 public SubgraphUnit() : base() { }
25
26 public SubgraphUnit(ScriptGraphAsset macro) : base(macro) { }
27
28 public static SubgraphUnit WithInputOutput()
29 {
30 var superUnit = new SubgraphUnit();
31 superUnit.nest.source = GraphSource.Embed;
32 superUnit.nest.embed = FlowGraph.WithInputOutput();
33 return superUnit;
34 }
35
36 public static SubgraphUnit WithStartUpdate()
37 {
38 var superUnit = new SubgraphUnit();
39 superUnit.nest.source = GraphSource.Embed;
40 superUnit.nest.embed = FlowGraph.WithStartUpdate();
41 return superUnit;
42 }
43
44 public override FlowGraph DefaultGraph()
45 {
46 return FlowGraph.WithInputOutput();
47 }
48
49 protected override void Definition()
50 {
51 isControlRoot = true; // TODO: Infer relations instead
52
53 // Using portDefinitions and type checks instead of specific definition collections
54 // to avoid duplicates. Iterating only once for speed.
55
56 foreach (var definition in nest.graph.validPortDefinitions)
57 {
58 if (definition is ControlInputDefinition)
59 {
60 var controlInputDefinition = (ControlInputDefinition)definition;
61 var key = controlInputDefinition.key;
62
63 ControlInput(key, (flow) =>
64 {
65 foreach (var unit in nest.graph.units)
66 {
67 if (unit is GraphInput)
68 {
69 var inputUnit = (GraphInput)unit;
70
71 flow.stack.EnterParentElement(this);
72
73 return inputUnit.controlOutputs[key];
74 }
75 }
76
77 return null;
78 });
79 }
80 else if (definition is ValueInputDefinition)
81 {
82 var valueInputDefinition = (ValueInputDefinition)definition;
83 var key = valueInputDefinition.key;
84 var type = valueInputDefinition.type;
85 var hasDefaultValue = valueInputDefinition.hasDefaultValue;
86 var defaultValue = valueInputDefinition.defaultValue;
87
88 var port = ValueInput(type, key);
89
90 if (hasDefaultValue)
91 {
92 port.SetDefaultValue(defaultValue);
93 }
94 }
95 else if (definition is ControlOutputDefinition)
96 {
97 var controlOutputDefinition = (ControlOutputDefinition)definition;
98 var key = controlOutputDefinition.key;
99
100 ControlOutput(key);
101 }
102 else if (definition is ValueOutputDefinition)
103 {
104 var valueOutputDefinition = (ValueOutputDefinition)definition;
105 var key = valueOutputDefinition.key;
106 var type = valueOutputDefinition.type;
107
108 ValueOutput(type, key, (flow) =>
109 {
110 flow.stack.EnterParentElement(this);
111
112 // Manual looping to avoid LINQ allocation
113 // Also removing check for multiple output nodes for speed
114 // (The first output node will be used without any error)
115
116 foreach (var unit in nest.graph.units)
117 {
118 if (unit is GraphOutput)
119 {
120 var outputUnit = (GraphOutput)unit;
121
122 var value = flow.GetValue(outputUnit.valueInputs[key]);
123
124 flow.stack.ExitParentElement();
125
126 return value;
127 }
128 }
129
130 flow.stack.ExitParentElement();
131
132 throw new InvalidOperationException("Missing output node when to get value.");
133 });
134 }
135 }
136 }
137
138 public void StartListening(GraphStack stack)
139 {
140 if (stack.TryEnterParentElement(this))
141 {
142 nest.graph.StartListening(stack);
143 stack.ExitParentElement();
144 }
145
146 stack.GetElementData<Data>(this).isListening = true;
147 }
148
149 public void StopListening(GraphStack stack)
150 {
151 stack.GetElementData<Data>(this).isListening = false;
152
153 if (stack.TryEnterParentElement(this))
154 {
155 nest.graph.StopListening(stack);
156 stack.ExitParentElement();
157 }
158 }
159
160 public bool IsListening(GraphPointer pointer)
161 {
162 return pointer.GetElementData<Data>(this).isListening;
163 }
164
165 #region Editing
166
167 public override void AfterAdd()
168 {
169 base.AfterAdd();
170
171 nest.beforeGraphChange += StopWatchingPortDefinitions;
172 nest.afterGraphChange += StartWatchingPortDefinitions;
173
174 StartWatchingPortDefinitions();
175 }
176
177 public override void BeforeRemove()
178 {
179 base.BeforeRemove();
180
181 StopWatchingPortDefinitions();
182
183 nest.beforeGraphChange -= StopWatchingPortDefinitions;
184 nest.afterGraphChange -= StartWatchingPortDefinitions;
185 }
186
187 private void StopWatchingPortDefinitions()
188 {
189 if (nest.graph != null)
190 {
191 nest.graph.onPortDefinitionsChanged -= Define;
192 }
193 }
194
195 private void StartWatchingPortDefinitions()
196 {
197 if (nest.graph != null)
198 {
199 nest.graph.onPortDefinitionsChanged += Define;
200 }
201 }
202
203 #endregion
204 }
205}