#region CPL License /* Nuclex Framework Copyright (C) 2002-2011 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library 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 IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Nuclex.Game.States { /// /// Game state that displays a loading screen while another state loads /// public class LoadingScreenState : GameState, IDisposable where LoadedGameStateType : IGameState, ILoadableGameState { /// Initializes a new loading screen game state /// /// Game state manager the loading screen state belongs to /// /// /// Game state that will be loaded by the loading screen /// public LoadingScreenState( IGameStateService gameStateService, LoadedGameStateType gameStateToLoad ) { this.gameStateService = gameStateService; this.gameStateToLoad = gameStateToLoad; this.progressChangedDelegate = new EventHandler( progressChanged ); } /// Immediately releases all resources used by the instance public void Dispose() { IAsyncResult asyncResult = this.asyncResult; if(asyncResult != null) { if(!asyncResult.IsCompleted) { #if NO_EXITCONTEXT asyncResult.AsyncWaitHandle.WaitOne(15000); #else asyncResult.AsyncWaitHandle.WaitOne(15000, false); #endif } } if(this.gameStateToLoad != null) { this.gameStateToLoad.ProgressChanged -= this.progressChangedDelegate; this.gameStateToLoad = default(LoadedGameStateType); } } /// /// Begins loading the loadable game state associated with the loading screen /// public void StartLoading() { bool redundantCall = (this.gameStateLoaded) || (this.asyncResult != null); if(redundantCall) { System.Diagnostics.Debug.WriteLine( "Warning: redundant call to LoadScreenState.StartLoading() encountered" ); return; } // Subscribe to the progress change event this.gameStateToLoad.ProgressChanged += this.progressChangedDelegate; // Begin loading the game state this loading screen was associated with this.asyncResult = this.gameStateToLoad.BeginLoad( new AsyncCallback(loadingCompleted), null ); if(this.asyncResult.CompletedSynchronously) { endLoading(); } } /// /// Allows the game state to run logic such as updating the world, /// checking for collisions, gathering input and playing audio. /// /// Provides a snapshot of timing values. public override void Update(GameTime gameTime) { if(this.gameStateLoaded) { // Game state is loaded, kill the loading screen and switch to the new state this.gameStateService.Switch(this.gameStateToLoad); } } /// Called when the load progress has changed /// New load progress protected virtual void OnLoadProgressChanged(float progress) { } /// Game state being loaded by the loading screen protected LoadedGameStateType GameStateToLoad { get { return this.gameStateToLoad; } } /// Ends the asynchronous loading operation /// /// Should only be called when the game state has notified us that loading is /// complete, otherwise, the caller will be blocked until loading actually completes. /// private void endLoading() { this.gameStateToLoad.EndLoad(this.asyncResult); // Unsubscribe from the progress change event this.gameStateToLoad.ProgressChanged -= this.progressChangedDelegate; this.asyncResult = null; } /// Called when the associated game state has finished loading /// Handle of the asynchronous loading operation private void loadingCompleted(IAsyncResult asyncResult) { this.gameStateLoaded = true; // Take into account that the game state may have completed its loading // synchronously, in which case the StartLoading() method will initiate // the endLoading() call. Not strictly neccessary here, since we're not // running a loop with the risk for endless recursion, but might be // preferrable for the implementer of ILoadableGameState.BeginLoad(). if(!asyncResult.CompletedSynchronously) { endLoading(); } } /// Called when the loading progress has changed /// Game state whose loading progress has changed /// Contains the new loading progress private void progressChanged(object sender, LoadProgressEventArgs arguments) { OnLoadProgressChanged(arguments.Progress); } /// Delegate we suscribe to the progress change notification private EventHandler progressChangedDelegate; /// Game state being loaded by the loading screen private LoadedGameStateType gameStateToLoad; /// private IGameStateService gameStateService; /// /// Whether the game state associated with the loading screen has been loaded already /// private volatile bool gameStateLoaded; /// Result handle for the asynchronous loading operation private volatile IAsyncResult asyncResult; } } // namespace Nuclex.Game.States