#region File Description
//-----------------------------------------------------------------------------
// LobbyScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Net;
#endregion
namespace NetworkStateManagement
{
///
/// The lobby screen provides a place for gamers to congregate before starting
/// the actual gameplay. It displays a list of all the gamers in the session,
/// and indicates which ones are currently talking. Each gamer can press a button
/// to mark themselves as ready: gameplay will begin after everyone has done this.
///
class LobbyScreen : GameScreen
{
#region Fields
NetworkSession networkSession;
Texture2D isReadyTexture;
Texture2D hasVoiceTexture;
Texture2D isTalkingTexture;
Texture2D voiceMutedTexture;
#endregion
#region Initialization
///
/// Constructs a new lobby screen.
///
public LobbyScreen (NetworkSession networkSession)
{
this.networkSession = networkSession;
TransitionOnTime = TimeSpan.FromSeconds (0.5);
TransitionOffTime = TimeSpan.FromSeconds (0.5);
}
///
/// Loads graphics content used by the lobby screen.
///
public override void LoadContent ()
{
ContentManager content = ScreenManager.Game.Content;
isReadyTexture = content.Load ("chat_ready");
hasVoiceTexture = content.Load ("chat_able");
isTalkingTexture = content.Load ("chat_talking");
voiceMutedTexture = content.Load ("chat_mute");
}
#endregion
#region Update
///
/// Updates the lobby screen.
///
public override void Update (GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update (gameTime, otherScreenHasFocus, coveredByOtherScreen);
if (!IsExiting) {
if (networkSession.SessionState == NetworkSessionState.Playing) {
// Check if we should leave the lobby and begin gameplay.
// We pass null as the controlling player, because the networked
// gameplay screen accepts input from any local players who
// are in the session, not just a single controlling player.
LoadingScreen.Load (ScreenManager, true, null,
new GameplayScreen (networkSession));
} else if (networkSession.IsHost && networkSession.IsEveryoneReady) {
// The host checks whether everyone has marked themselves
// as ready, and starts the game in response.
networkSession.StartGame ();
}
}
}
///
/// Handles user input for all the local gamers in the session. Unlike most
/// screens, which use the InputState class to combine input data from all
/// gamepads, the lobby needs to individually mark specific players as ready,
/// so it loops over all the local gamers and reads their inputs individually.
///
public override void HandleInput (InputState input)
{
foreach (LocalNetworkGamer gamer in networkSession.LocalGamers) {
PlayerIndex playerIndex = gamer.SignedInGamer.PlayerIndex;
PlayerIndex unwantedOutput;
if (input.IsMenuSelect (playerIndex, out unwantedOutput)) {
HandleMenuSelect (gamer);
} else if (input.IsMenuCancel (playerIndex, out unwantedOutput)) {
HandleMenuCancel (gamer);
}
}
}
///
/// Handle MenuSelect inputs by marking ourselves as ready.
///
void HandleMenuSelect (LocalNetworkGamer gamer)
{
if (!gamer.IsReady) {
gamer.IsReady = true;
} else if (gamer.IsHost) {
// The host has an option to force starting the game, even if not
// everyone has marked themselves ready. If they press select twice
// in a row, the first time marks the host ready, then the second
// time we ask if they want to force start.
MessageBoxScreen messageBox = new MessageBoxScreen (
Resources.ConfirmForceStartGame);
messageBox.Accepted += ConfirmStartGameMessageBoxAccepted;
ScreenManager.AddScreen (messageBox, gamer.SignedInGamer.PlayerIndex);
}
}
///
/// Event handler for when the host selects ok on the "are you sure
/// you want to start even though not everyone is ready" message box.
///
void ConfirmStartGameMessageBoxAccepted (object sender, PlayerIndexEventArgs e)
{
if (networkSession.SessionState == NetworkSessionState.Lobby) {
networkSession.StartGame ();
}
}
///
/// Handle MenuCancel inputs by clearing our ready status, or if it is
/// already clear, prompting if the user wants to leave the session.
///
void HandleMenuCancel (LocalNetworkGamer gamer)
{
if (gamer.IsReady) {
gamer.IsReady = false;
} else {
PlayerIndex playerIndex = gamer.SignedInGamer.PlayerIndex;
NetworkSessionComponent.LeaveSession (ScreenManager, playerIndex);
}
}
#endregion
#region Draw
///
/// Draws the lobby screen.
///
public override void Draw (GameTime gameTime)
{
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Font;
Vector2 position = new Vector2 (100, 150);
// Make the lobby slide into place during transitions.
float transitionOffset = (float)Math.Pow (TransitionPosition, 2);
if (ScreenState == ScreenState.TransitionOn)
position.X -= transitionOffset * 256;
else
position.X += transitionOffset * 512;
spriteBatch.Begin ();
// Draw all the gamers in the session.
int gamerCount = 0;
foreach (NetworkGamer gamer in networkSession.AllGamers) {
DrawGamer (gamer, position);
// Advance to the next screen position, wrapping into two
// columns if there are more than 8 gamers in the session.
if (++gamerCount == 8) {
position.X += 433;
position.Y = 150;
} else
position.Y += ScreenManager.Font.LineSpacing;
}
// Draw the screen title.
string title = Resources.Lobby;
Vector2 titlePosition = new Vector2 (533, 80);
Vector2 titleOrigin = font.MeasureString (title) / 2;
Color titleColor = new Color (192, 192, 192) * TransitionAlpha;
float titleScale = 1.25f;
titlePosition.Y -= transitionOffset * 100;
spriteBatch.DrawString (font, title, titlePosition, titleColor, 0,
titleOrigin, titleScale, SpriteEffects.None, 0);
spriteBatch.End ();
}
///
/// Helper draws the gamertag and status icons for a single NetworkGamer.
///
void DrawGamer (NetworkGamer gamer, Vector2 position)
{
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Font;
Vector2 iconWidth = new Vector2 (34, 0);
Vector2 iconOffset = new Vector2 (0, 12);
Vector2 iconPosition = position + iconOffset;
// Draw the "is ready" icon.
if (gamer.IsReady) {
spriteBatch.Draw (isReadyTexture, iconPosition,
Color.Lime * TransitionAlpha);
}
iconPosition += iconWidth;
// Draw the "is muted", "is talking", or "has voice" icon.
if (gamer.IsMutedByLocalUser) {
spriteBatch.Draw (voiceMutedTexture, iconPosition,
Color.Red * TransitionAlpha);
} else if (gamer.IsTalking) {
spriteBatch.Draw (isTalkingTexture, iconPosition,
Color.Yellow * TransitionAlpha);
} else if (gamer.HasVoice) {
spriteBatch.Draw (hasVoiceTexture, iconPosition,
Color.White * TransitionAlpha);
}
// Draw the gamertag, normally in white, but yellow for local players.
string text = gamer.Gamertag;
if (gamer.IsHost)
text += Resources.HostSuffix;
Color color = (gamer.IsLocal) ? Color.Yellow : Color.White;
spriteBatch.DrawString (font, text, position + iconWidth * 2,
color * TransitionAlpha);
}
#endregion
}
}