146 lines
6.6 KiB
C#

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<string, IState> states;
/// <summary>
/// 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.
/// </summary>
/// <param name="initialState">The name of the initial state.</param>
/// <param name="states">The initial set of game states to be added.</param>
internal StateManager() {
backgroundColour = Color.Orange;
this.states = new ConcurrentDictionary<string, IState>();
}
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();
}
/// <summary>
/// Begins a state change. The current stage will be notified, and so will the state that is being changed to.
/// </summary>
/// <param name="name">The name of the state that we are turning to.</param>
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.");
}
/// <summary>
/// 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.
/// </summary>
/// <param name="states">The states to add.</param>
/// <returns>True if all the states were unique, and false if there were repetitions determined by name.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="state">The state to be added to this manager.</param>
/// <returns>False if a state of this name has already been registered.</returns>
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;
}
/// <summary>
/// Removes the state given the name of the state.
/// If the state doesn't exist, nothing happens.
/// Will call dispose on the state.
/// </summary>
/// <param name="name">The name of the state.</param>
/// <returns>False if the state is being used, or the state doesn't exist.</returns>
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);
}
/// <summary>
/// Removes all the states in this state manager except for the current one.
/// Deinitializes the removed states.
/// </summary>
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.");
}
}
}