A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEngine;
5
6namespace Unity.VisualScripting
7{
8 [SerializationVersion("A")]
9 public sealed class StateGraph : Graph, IGraphEventListener
10 {
11 public StateGraph()
12 {
13 states = new GraphElementCollection<IState>(this);
14 transitions = new GraphConnectionCollection<IStateTransition, IState, IState>(this);
15 groups = new GraphElementCollection<GraphGroup>(this);
16 sticky = new GraphElementCollection<StickyNote>(this);
17
18 elements.Include(states);
19 elements.Include(transitions);
20 elements.Include(groups);
21 elements.Include(sticky);
22 }
23
24 public override IGraphData CreateData()
25 {
26 return new StateGraphData(this);
27 }
28
29 public void StartListening(GraphStack stack)
30 {
31 stack.GetGraphData<StateGraphData>().isListening = true;
32
33 var activeStates = GetActiveStatesNoAlloc(stack);
34
35 foreach (var state in activeStates)
36 {
37 (state as IGraphEventListener)?.StartListening(stack);
38 }
39
40 activeStates.Free();
41 }
42
43 public void StopListening(GraphStack stack)
44 {
45 var activeStates = GetActiveStatesNoAlloc(stack);
46
47 foreach (var state in activeStates)
48 {
49 (state as IGraphEventListener)?.StopListening(stack);
50 }
51
52 activeStates.Free();
53
54 stack.GetGraphData<StateGraphData>().isListening = false;
55 }
56
57 public bool IsListening(GraphPointer pointer)
58 {
59 return pointer.GetGraphData<StateGraphData>().isListening;
60 }
61
62 #region Elements
63
64 [DoNotSerialize]
65 public GraphElementCollection<IState> states { get; internal set; }
66
67 [DoNotSerialize]
68 public GraphConnectionCollection<IStateTransition, IState, IState> transitions { get; internal set; }
69
70 [DoNotSerialize]
71 public GraphElementCollection<GraphGroup> groups { get; internal set; }
72
73 [DoNotSerialize]
74 public GraphElementCollection<StickyNote> sticky { get; private set; }
75 #endregion
76
77
78 #region Lifecycle
79
80 // Active state detection happens twice:
81 //
82 // 1. Before the enumeration, because any state
83 // that becomes active during an update shouldn't
84 // be updated until the next update
85 //
86 // 2. Inside the update method, because a state
87 // that was active during enumeration and no longer
88 // is shouldn't be updated.
89
90 private HashSet<IState> GetActiveStatesNoAlloc(GraphPointer pointer)
91 {
92 var activeStates = HashSetPool<IState>.New();
93
94 foreach (var state in states)
95 {
96 var stateData = pointer.GetElementData<State.Data>(state);
97
98 if (stateData.isActive)
99 {
100 activeStates.Add(state);
101 }
102 }
103
104 return activeStates;
105 }
106
107 public void Start(Flow flow)
108 {
109 flow.stack.GetGraphData<StateGraphData>().isListening = true;
110
111 foreach (var state in states.Where(s => s.isStart))
112 {
113 try
114 {
115 state.OnEnter(flow, StateEnterReason.Start);
116 }
117 catch (Exception ex)
118 {
119 state.HandleException(flow.stack, ex);
120 throw;
121 }
122 }
123 }
124
125 public void Stop(Flow flow)
126 {
127 var activeStates = GetActiveStatesNoAlloc(flow.stack);
128
129 foreach (var state in activeStates)
130 {
131 try
132 {
133 state.OnExit(flow, StateExitReason.Stop);
134 }
135 catch (Exception ex)
136 {
137 state.HandleException(flow.stack, ex);
138 throw;
139 }
140 }
141
142 activeStates.Free();
143
144 flow.stack.GetGraphData<StateGraphData>().isListening = false;
145 }
146
147 #endregion
148
149
150 public static StateGraph WithStart()
151 {
152 var stateGraph = new StateGraph();
153
154 var startState = FlowState.WithEnterUpdateExit();
155 startState.isStart = true;
156 startState.nest.embed.title = "Start";
157 startState.position = new Vector2(-86, -15);
158
159 stateGraph.states.Add(startState);
160
161 return stateGraph;
162 }
163 }
164}