A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEditor; 5using UnityEngine; 6 7namespace Unity.VisualScripting 8{ 9 [Canvas(typeof(StateGraph))] 10 public sealed class StateCanvas : VisualScriptingCanvas<StateGraph> 11 { 12 public StateCanvas(StateGraph graph) : base(graph) { } 13 14 15 #region View 16 17 protected override bool shouldEdgePan => base.shouldEdgePan || isCreatingTransition; 18 19 #endregion 20 21 22 #region Drawing 23 24 protected override void DrawBackground() 25 { 26 base.DrawBackground(); 27 28 if (isCreatingTransition) 29 { 30 var startRect = this.Widget(transitionSource).position; 31 var end = mousePosition; 32 33 Edge startEdge, endEdge; 34 35 GraphGUI.GetConnectionEdge 36 ( 37 startRect.center, 38 end, 39 out startEdge, 40 out endEdge 41 ); 42 43 var start = startRect.GetEdgeCenter(startEdge); 44 45 GraphGUI.DrawConnectionArrow(Color.white, start, end, startEdge, endEdge); 46 } 47 } 48 49 #endregion 50 51 52 #region Clipboard 53 54 public override void ShrinkCopyGroup(HashSet<IGraphElement> copyGroup) 55 { 56 copyGroup.RemoveWhere(element => 57 { 58 if (element is IStateTransition) 59 { 60 var transition = (IStateTransition)element; 61 62 if (!copyGroup.Contains(transition.source) || 63 !copyGroup.Contains(transition.destination)) 64 { 65 return true; 66 } 67 } 68 69 return false; 70 }); 71 } 72 73 #endregion 74 75 76 #region Window 77 78 public override void OnToolbarGUI() 79 { 80 if (graph.states.Any(u => u.GetException(reference) != null) || graph.transitions.Any(t => t.GetException(reference) != null)) 81 { 82 if (GUILayout.Button("Clear Errors", LudiqStyles.toolbarButton)) 83 { 84 foreach (var state in graph.states) 85 { 86 state.SetException(reference, null); 87 } 88 89 foreach (var transition in graph.transitions) 90 { 91 transition.SetException(reference, null); 92 } 93 } 94 } 95 96 EditorGUI.BeginChangeCheck(); 97 98 BoltCore.Configuration.dimInactiveNodes = GUILayout.Toggle(BoltCore.Configuration.dimInactiveNodes, "Dim", LudiqStyles.toolbarButton); 99 100 if (EditorGUI.EndChangeCheck()) 101 { 102 BoltCore.Configuration.Save(); 103 } 104 105 base.OnToolbarGUI(); 106 } 107 108 #endregion 109 110 111 #region Context 112 113 protected override void OnContext() 114 { 115 if (isCreatingTransition) 116 { 117 CancelTransition(); 118 } 119 else 120 { 121 base.OnContext(); 122 } 123 } 124 125 protected override IEnumerable<DropdownOption> GetContextOptions() 126 { 127 yield return new DropdownOption((Action<Vector2>)CreateFlowState, "Create Script State"); 128 yield return new DropdownOption((Action<Vector2>)CreateSuperState, "Create Super State"); 129 yield return new DropdownOption((Action<Vector2>)CreateAnyState, "Create Any State"); 130 yield return new DropdownOption((Action<Vector2>)(NewSticky), "Create Sticky Note"); 131 foreach (var baseOption in base.GetContextOptions()) 132 { 133 yield return baseOption; 134 } 135 } 136 137 private void CreateFlowState(Vector2 position) 138 { 139 var flowState = FlowState.WithEnterUpdateExit(); 140 141 if (!graph.states.Any()) 142 { 143 flowState.isStart = true; 144 flowState.nest.embed.title = "Start"; 145 } 146 147 AddState(flowState, position); 148 } 149 150 private void CreateSuperState(Vector2 position) 151 { 152 var superState = SuperState.WithStart(); 153 154 if (!graph.states.Any()) 155 { 156 superState.isStart = true; 157 superState.nest.embed.title = "Start"; 158 } 159 160 AddState(superState, position); 161 } 162 163 private void CreateAnyState(Vector2 position) 164 { 165 AddState(new AnyState(), position); 166 } 167 168 private void NewSticky(Vector2 position) 169 { 170 var stickyNote = new StickyNote() { position = new Rect(position, new Vector2(100, 100)) }; 171 graph.elements.Add(stickyNote); 172 selection.Select(stickyNote); 173 GUI.changed = true; 174 } 175 176 public void AddState(IState state, Vector2 position) 177 { 178 UndoUtility.RecordEditedObject("Create State"); 179 state.position = position; 180 graph.states.Add(state); 181 state.position -= this.Widget(state).position.size / 2; 182 state.position = state.position.PixelPerfect(); 183 this.Widget(state).Reposition(); 184 selection.Select(state); 185 GUI.changed = true; 186 } 187 188 #endregion 189 190 191 #region Lifecycle 192 193 public override void Close() 194 { 195 base.Close(); 196 197 CancelTransition(); 198 } 199 200 protected override void HandleHighPriorityInput() 201 { 202 if (isCreatingTransition) 203 { 204 if (e.IsMouseDrag(MouseButton.Left)) 205 { 206 // Priority over lasso 207 e.Use(); 208 } 209 else if (e.IsKeyDown(KeyCode.Escape)) 210 { 211 CancelTransition(); 212 e.Use(); 213 } 214 if (e.IsMouseDown(MouseButton.Left) || e.IsMouseUp(MouseButton.Left)) 215 { 216 CompleteTransitionToNewState(); 217 e.Use(); 218 } 219 } 220 221 base.HandleHighPriorityInput(); 222 } 223 224 public void CompleteTransitionToNewState() 225 { 226 var startRect = this.Widget(transitionSource).position; 227 var end = mousePosition; 228 229 GraphGUI.GetConnectionEdge 230 ( 231 startRect.center, 232 end, 233 out var startEdge, 234 out var endEdge 235 ); 236 237 var destination = FlowState.WithEnterUpdateExit(); 238 graph.states.Add(destination); 239 240 Vector2 offset; 241 242 var size = this.Widget(destination).position.size; 243 244 switch (endEdge) 245 { 246 case Edge.Left: 247 offset = new Vector2(0, -size.y / 2); 248 break; 249 case Edge.Right: 250 offset = new Vector2(-size.x, -size.y / 2); 251 break; 252 case Edge.Top: 253 offset = new Vector2(-size.x / 2, 0); 254 break; 255 case Edge.Bottom: 256 offset = new Vector2(-size.x / 2, -size.y); 257 break; 258 default: 259 throw new UnexpectedEnumValueException<Edge>(endEdge); 260 } 261 262 destination.position = mousePosition + offset; 263 264 destination.position = destination.position.PixelPerfect(); 265 266 EndTransition(destination); 267 } 268 269 #endregion 270 271 272 #region Drag & Drop 273 274 public override bool AcceptsDragAndDrop() 275 { 276 return DragAndDropUtility.Is<ScriptGraphAsset>() || DragAndDropUtility.Is<StateGraphAsset>(); 277 } 278 279 public override void PerformDragAndDrop() 280 { 281 if (DragAndDropUtility.Is<ScriptGraphAsset>()) 282 { 283 var flowMacro = DragAndDropUtility.Get<ScriptGraphAsset>(); 284 var flowState = new FlowState(flowMacro); 285 AddState(flowState, DragAndDropUtility.position); 286 } 287 else if (DragAndDropUtility.Is<StateGraphAsset>()) 288 { 289 var asset = DragAndDropUtility.Get<StateGraphAsset>(); 290 var superState = new SuperState(asset); 291 AddState(superState, DragAndDropUtility.position); 292 } 293 } 294 295 public override void DrawDragAndDropPreview() 296 { 297 if (DragAndDropUtility.Is<ScriptGraphAsset>()) 298 { 299 GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, DragAndDropUtility.Get<ScriptGraphAsset>().name, typeof(ScriptGraphAsset).Icon()); 300 } 301 else if (DragAndDropUtility.Is<StateGraphAsset>()) 302 { 303 GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, DragAndDropUtility.Get<StateGraphAsset>().name, typeof(StateGraphAsset).Icon()); 304 } 305 } 306 307 #endregion 308 309 310 #region Transition Creation 311 312 public IState transitionSource { get; set; } 313 314 public bool isCreatingTransition => transitionSource != null; 315 316 public void StartTransition(IState source) 317 { 318 transitionSource = source; 319 window.Focus(); 320 } 321 322 public void EndTransition(IState destination) 323 { 324 UndoUtility.RecordEditedObject("Create State Transition"); 325 326 var transition = FlowStateTransition.WithDefaultTrigger(transitionSource, destination); 327 graph.transitions.Add(transition); 328 transitionSource = null; 329 this.Widget(transition).BringToFront(); 330 selection.Select(transition); 331 GUI.changed = true; 332 } 333 334 public void CancelTransition() 335 { 336 transitionSource = null; 337 } 338 339 #endregion 340 } 341}