/*
* Util.MainGame.cs
* Authors: Adam Nabinger
* Copyright (c) 2007-2008 Cornell University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace Util
{
///
/// A Class to act as the Main Game, hosting one or many stackable game screens.
///
public sealed class MainGame : Game
{
#region Constants
///
/// The subdirectory under the dll, where the content is stored.
///
public const string ContentSubdirectory = @"\Content\";
#endregion
#region Fields
// The stack of game screens used for determining what screen has focus and needs updating/rendering
private readonly Stack Screens = new Stack();
// Whether or not this has been initialized
private static bool initialized;
// Whether or not this has been disposed
private static bool disposed;
// Whether or not the error logger has been initialized
private static bool errorLoggerInitialized;
// The logger used for recording any error messaged that come up
private static Logger errorLogger;
///
/// The graphics device manager
///
internal GraphicsDeviceManager gdm;
///
/// The main game's reference to itself.
///
internal static MainGame TheGame;
#endregion
#region Creation
///
/// Constructs a new main game.
///
public MainGame()
{
gdm = new GraphicsDeviceManager(this);
#if DEBUG
// Intercept the graphics device creation event
gdm.PreparingDeviceSettings += graphics_PreparingDeviceSettings;
#endif
Content.RootDirectory = "Content";
TheGame = this;
}
#endregion
#region Screen Stack Modifiers
///
/// Specify the screen that the main game should start with.
///
/// The game screen that the main game starts with.
public void StartScreen(GameScreen screen)
{
if (screen.RootDirectory == null)
{
screen.RootDirectory = ProjectInfo.HomeDirectory;
}
PushScreen(screen);
}
///
/// Sets the DrawOrder and UpdateOrder of the new Screen to place it on top of the stack.
/// Then, Initialize and LoadContent will be called on the newScreen.
/// Finally, the new Screen will enter into the common Update+Draw loop.
///
/// The new game screen to add to the top of the stack
internal void PushScreen(GameScreen newScreen)
{
newScreen.mGame = this;
newScreen.Content = new VariableContentManager(Services, newScreen.RootDirectory + ContentSubdirectory);
if (Screens.Count > 0)
{
newScreen.DrawOrder = Screens.Peek().DrawOrder + 10;
newScreen.UpdateOrder = Screens.Peek().UpdateOrder + 10;
}
Screens.Push(newScreen);
Components.Add(newScreen);
}
///
/// Remove the screen that is on the top of the stack.
/// Dispose, and UnloadContent are called.
/// Then, the screen below, which is now on top again, gets Reinitialized.
///
internal void PopScreen()
{
if (Screens.Count > 1)
{
GameScreen oldScreen = Screens.Pop();
oldScreen.Dispose();
Screens.Peek().Reinitialize();
}
else
{
Exit();
}
}
///
/// Gets rid of the current game screen and switches to the given one
///
/// The new game screen to switch to
internal void SwapScreen(GameScreen newScreen)
{
PopScreen();
PushScreen(newScreen);
}
#endregion
#region Initialization
///
/// Initialize.
///
protected override void Initialize()
{
initialized = true;
base.Initialize();
}
#endregion
#region Management
///
/// Dispose of the main game.
///
/// Whether or not the main game is currently being disposed of.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "gdm")]
protected override void Dispose(bool disposing)
{
if (errorLoggerInitialized)
{
errorLogger.Dispose();
errorLoggerInitialized = false;
}
if (initialized && !disposed)
{
disposed = true;
if (disposing)
{
while (Screens.Count >= 1)
{
Screens.Pop().Dispose();
}
}
}
base.Dispose(disposing);
}
#endregion
#region Error Logging
///
/// Static access to the logger to be used sparingly to log spontaneous exceptions,
/// as many occur before the local logger is available.
///
/// The error message to log.
public static void LogError(string errorMessage)
{
if (!errorLoggerInitialized)
{
errorLogger = new Logger(GameScreen.GlobalRootStorageDirectory + "ErrorLog.txt", false);
errorLogger.Initialize();
errorLoggerInitialized = true;
}
errorLogger.Write((int)EventCode.Error, errorMessage);
}
#endregion
#region Util
///
/// Get a stack trace of all active screens, for debugging.
///
public string GetStackTrace()
{
StringBuilder ret = new StringBuilder();
ret.AppendLine("MainGame GameScreen Stack Trace:");
foreach (GameScreen screen in Screens)
{
ret.AppendLine(screen.ToString());
}
return ret.ToString();
}
#if DEBUG
///
/// Instrument the graphics device.
///
///
///
void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
foreach (GraphicsAdapter curAdapter in GraphicsAdapter.Adapters)
{
if (curAdapter.Description.Contains("PerfHUD"))
{
e.GraphicsDeviceInformation.Adapter = curAdapter;
e.GraphicsDeviceInformation.DeviceType = DeviceType.Reference;
break;
}
}
return;
}
#endif
#endregion
}
}