using System; using System.Collections.Generic; using System.Drawing; using System.Threading; using SlatedGameToolkit.Framework.Graphics.Window; using SlatedGameToolkit.Framework.StateSystem.States; using SlatedGameToolkit.Framework.Utilities; using SlatedGameToolkit.Framework.Graphics.OpenGL; using SlatedGameToolkit.Framework.Logging; using System.Collections.Concurrent; namespace SlatedGameToolkit.Framework.StateSystem { public sealed class StateManager { public Thread thread; public Color backgroundColour; private IState currentState; private IState nextState; private ConcurrentDictionary states; /// /// Instantiates a game state manager with an initial state, and a set of states to be added at the start. /// States can later be added or removed. /// None of the parameters can be null, and the initial state must exist in initial set of states. /// /// The name of the initial state. /// The initial set of game states to be added. internal StateManager() { backgroundColour = Color.Orange; this.states = new ConcurrentDictionary(); } internal void Initialize(IState initialState) { if (initialState == null) throw new ArgumentNullException("initialState"); Logger.Log("Initialized state manager with state: " + initialState.getName()); thread = Thread.CurrentThread; AddState(initialState); currentState = initialState; currentState.Activate(); } internal void Update(double timeStep) { if (nextState != null) { if (currentState.Deactivate() & nextState.Activate()) { currentState = nextState; nextState = null; } } currentState.Update(timeStep); } internal void Render(double delta) { WindowContext windowContext = WindowContextsManager.CurrentWindowContext; windowContext.Context.ClearColor(backgroundColour.RedAsFloat(), backgroundColour.GreenAsFloat(), backgroundColour.BlueAsFloat(), 1f); windowContext.Context.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); currentState.Render(delta); windowContext.SwapBuffer(); } /// /// Begins a state change. The current stage will be notified, and so will the state that is being changed to. /// /// The name of the state that we are turning to. public void ChangeState(string name) { if (thread != Thread.CurrentThread) throw new ThreadStateException("State cannot be changed from a different thread."); if (!states.TryGetValue(name, out nextState)) throw new ArgumentException("The requested state to change to does not exist in this manager."); } /// /// Adds the given states to this manager under each of their own respective names. /// Initializes all the states. /// If there are two states of the same name, the first state in the array is added and the second is discarded. /// The discarded state will not be initialized and dispose will not be called on it. /// /// The states to add. /// True if all the states were unique, and false if there were repetitions determined by name. public bool AddStates(IState[] states) { if (states == null || states.Length == 0) throw new ArgumentException("The array of states cannot be null, and cannot be of size 0"); bool unique = true; foreach (IState state in states) { unique = unique && AddState(state); } return unique; } /// /// Adds the given state to this manager under it's own name. /// Initializes the state as well. /// Will not initialize the state if this method returns false. /// /// The state to be added to this manager. /// False if a state of this name has already been registered. public bool AddState(IState state) { if (thread != Thread.CurrentThread) throw new ThreadStateException("Cannot add a state from a different thread."); Logger.Log("Adding state: " + state.getName(), LogLevel.DEBUG); if (!this.states.TryAdd(state.getName(), state)) { Logger.Log(string.Format("State with name \"{0}\" already exists in manager.", state.getName()), LogLevel.WARNING); } state.Initialize(this); return true; } /// /// Removes the state given the name of the state. /// If the state doesn't exist, nothing happens. /// Will call dispose on the state. /// /// The name of the state. /// False if the state is being used, or the state doesn't exist. public bool RemoveState(string name) { if (thread != Thread.CurrentThread) throw new ThreadStateException("Cannot remove a state from a different thread."); if (states[name] == currentState) return false; IState state = states[name]; Logger.Log("Removing state: " + name, LogLevel.DEBUG); try { state.Deinitialize(); Logger.Log("State deinitialized: " + name, LogLevel.DEBUG); } catch (Exception e) { Logger.Log(e.ToString(), LogLevel.WARNING); Logger.Log("Failed to deinitialize state: " + state.getName(), LogLevel.WARNING); } IState removedState; return states.Remove(name, out removedState); } /// /// Removes all the states in this state manager except for the current one. /// Deinitializes the removed states. /// public void RemoveAllStates() { foreach (String state in this.states.Keys) { RemoveState(state); } } internal void Deinitialize() { if (currentState == null) return; currentState = null; RemoveAllStates(); Logger.Log("Deinitializing state manager."); } } }