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}