#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