/* * 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 } }