#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.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Net;
#endregion
namespace NetRumble
{
///
/// The lobby screen is shows the players in the game, outside the gameplay.
///
public class LobbyScreen : MenuScreen, IDisposable
{
#region Constants
///
/// The instructions shown to the player at the bottom of the lobby screen.
///
const string instructions =
"Press X to mark/unmark ready, LB/RB to toggle color, LT/RT to toggle ship";
#endregion
#region Gameplay Data
///
/// The primary object for this game.
///
private World world;
#endregion
#region Networking Data
///
/// The network session for this game.
///
private NetworkSession networkSession;
///
/// The packet writer used to send data from this screen.
///
private PacketWriter packetWriter = new PacketWriter();
///
/// Event handler for the session-ended event.
///
EventHandler sessionEndedHandler;
///
/// Event handler for the game-ended event.
///
EventHandler gameStartedHandler;
///
/// Event handler for the gamer-left event.
///
EventHandler gamerJoinedHandler;
#endregion
#region Initialization
///
/// Constructs a new LobbyScreen object.
///
public LobbyScreen(NetworkSession networkSession) : base()
{
// safety-check the parameter
if (networkSession == null)
{
throw new ArgumentNullException("networkSession");
}
// apply the parameters
this.networkSession = networkSession;
// add the single menu entry
MenuEntries.Add("");
// set the transition time
TransitionOnTime = TimeSpan.FromSeconds(1.0);
TransitionOffTime = TimeSpan.FromSeconds(0.0);
gamerJoinedHandler = new EventHandler(
networkSession_GamerJoined);
gameStartedHandler = new EventHandler(
networkSession_GameStarted);
sessionEndedHandler = new EventHandler(
networkSession_SessionEnded);
}
///
/// Load graphics content for the game.
///
public override void LoadContent()
{
base.LoadContent();
// create the world object
world = new World(ScreenManager.GraphicsDevice, ScreenManager.Content,
networkSession);
// set the networking events
networkSession.GamerJoined += gamerJoinedHandler;
networkSession.GameStarted += gameStartedHandler;
networkSession.SessionEnded += sessionEndedHandler;
}
#endregion
#region Updating Methods
///
/// Updates the lobby. This method checks the GameScreen.IsActive
/// property, so the game will stop updating when the pause menu is active,
/// or if you tab away to a different application.
///
public override void Update(Microsoft.Xna.Framework.GameTime gameTime,
bool otherScreenHasFocus, bool coveredByOtherScreen)
{
if (networkSession != null)
{
// update the network session
try
{
networkSession.Update();
}
catch (NetworkException ne)
{
System.Console.WriteLine(
"Network failed to update: " + ne.ToString());
if (networkSession != null)
{
networkSession.Dispose();
networkSession = null;
}
}
}
// update the world
if ((world != null) && !otherScreenHasFocus && !coveredByOtherScreen)
{
if (world.GameWon)
{
// unload the existing world
world.Dispose();
world = null;
// make sure that all of the ships have cleaned up
foreach (NetworkGamer networkGamer in networkSession.AllGamers)
{
PlayerData playerData = networkGamer.Tag as PlayerData;
if ((playerData != null) && (playerData.Ship != null))
{
playerData.Ship.Die(null, true);
}
}
// make sure the collision manager is up-to-date
CollisionManager.Collection.ApplyPendingRemovals();
// create a new world
world = new World(ScreenManager.GraphicsDevice,
ScreenManager.Content, networkSession);
}
else if (world.GameExited)
{
if (!IsExiting)
{
ExitScreen();
}
if (world != null)
{
world.Dispose();
world = null;
}
if (networkSession != null)
{
networkSession.Dispose();
networkSession = null;
}
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
return;
}
else
{
world.Update((float)gameTime.ElapsedGameTime.TotalSeconds, true);
}
}
// update the menu entry text
if (otherScreenHasFocus == false)
{
if ((networkSession.LocalGamers.Count > 0) &&
(networkSession.SessionState == NetworkSessionState.Lobby))
{
if (!networkSession.LocalGamers[0].IsReady)
{
MenuEntries[0] = "Press X to Mark as Ready";
}
else if (!networkSession.IsEveryoneReady)
{
MenuEntries[0] = "Waiting for all players to mark as ready...";
}
else if (!networkSession.IsHost)
{
MenuEntries[0] = "Waiting for the host to start game...";
}
else
{
MenuEntries[0] = "Starting the game...";
networkSession.StartGame();
}
}
else if (networkSession.SessionState == NetworkSessionState.Playing)
{
MenuEntries[0] = "Game starting...";
}
// if the game is playing and the world is initialized, then start up
if ((networkSession.SessionState == NetworkSessionState.Playing) &&
(world != null) && world.Initialized)
{
GameplayScreen gameplayScreen =
new GameplayScreen(networkSession, world);
gameplayScreen.ScreenManager = this.ScreenManager;
ScreenManager.AddScreen(gameplayScreen);
}
}
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
}
///
/// Lets the game respond to player input. Unlike the Update method,
/// this will only be called when the gameplay screen is active.
///
public override void HandleInput(InputState input)
{
// safety-check the parameter
if (input == null)
{
throw new ArgumentNullException("input");
}
if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
{
// update the ready state
if (input.MarkReady)
{
networkSession.LocalGamers[0].IsReady =
!networkSession.LocalGamers[0].IsReady;
}
// update the player data
PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData;
if (playerData != null)
{
bool playerDataChanged = false;
if (input.ShipColorChangeUp)
{
playerData.ShipColor = Ship.GetNextUniqueColorIndex(
playerData.ShipColor, networkSession);
playerDataChanged = true;
}
else if (input.ShipColorChangeDown)
{
playerData.ShipColor = Ship.GetPreviousUniqueColorIndex(
playerData.ShipColor, networkSession);
playerDataChanged = true;
}
if (input.ShipModelChangeUp)
{
playerData.ShipVariation =
(byte)((playerData.ShipVariation + 1) % 4);
playerDataChanged = true;
}
else if (input.ShipModelChangeDown)
{
if (playerData.ShipVariation == 0)
{
playerData.ShipVariation = 3;
}
else
{
playerData.ShipVariation--;
}
playerDataChanged = true;
}
// if the data changed, send an update to the others
if (playerDataChanged)
{
packetWriter.Write((int)World.PacketTypes.PlayerData);
playerData.Serialize(packetWriter);
networkSession.LocalGamers[0].SendData(packetWriter,
SendDataOptions.ReliableInOrder);
}
}
}
base.HandleInput(input);
}
///
/// Responds to user menu selections.
///
protected override void OnSelectEntry(int entryIndex) { }
///
/// Force the end of a network session so that a new one can be joined.
///
public void EndSession()
{
if (networkSession != null)
{
networkSession.Dispose();
networkSession = null;
}
}
///
/// Exit this screen.
///
public override void ExitScreen()
{
if (!IsExiting && (networkSession != null))
{
networkSession.GamerJoined -= gamerJoinedHandler;
networkSession.GameStarted -= gameStartedHandler;
networkSession.SessionEnded -= sessionEndedHandler;
}
base.ExitScreen();
}
///
/// Screen-specific update to gamer rich presence.
///
public override void UpdatePresence()
{
if (!IsExiting && (networkSession != null))
{
foreach (LocalNetworkGamer localGamer in networkSession.LocalGamers)
{
SignedInGamer signedInGamer = localGamer.SignedInGamer;
if (signedInGamer.IsSignedInToLive)
{
if (networkSession.IsHost)
{
signedInGamer.Presence.PresenceMode = GamerPresenceMode.WaitingForPlayers;
}
else
{
signedInGamer.Presence.PresenceMode = GamerPresenceMode.WaitingInLobby;
}
}
}
}
}
#endregion
#region Drawing Methods
///
/// Draw the lobby screen.
///
///
public override void Draw(Microsoft.Xna.Framework.GameTime gameTime)
{
// draw in four columns
Vector2[] columnPositions = new Vector2[4];
columnPositions[0] = new Vector2(
ScreenManager.GraphicsDevice.Viewport.Width * 0.2f,
ScreenManager.GraphicsDevice.Viewport.Height * 0.70f);
columnPositions[1] = new Vector2(
ScreenManager.GraphicsDevice.Viewport.Width * 0.4f,
ScreenManager.GraphicsDevice.Viewport.Height * 0.70f);
columnPositions[2] = new Vector2(
ScreenManager.GraphicsDevice.Viewport.Width * 0.6f,
ScreenManager.GraphicsDevice.Viewport.Height * 0.70f);
columnPositions[3] = new Vector2(
ScreenManager.GraphicsDevice.Viewport.Width * 0.8f,
ScreenManager.GraphicsDevice.Viewport.Height * 0.70f);
ScreenManager.SpriteBatch.Begin();
// draw all of the players data
if (networkSession != null)
{
for (int i = 0; i < networkSession.AllGamers.Count; i++)
{
world.DrawPlayerData((float)gameTime.TotalGameTime.TotalSeconds,
networkSession.AllGamers[i], columnPositions[i % 4],
ScreenManager.SpriteBatch, true);
columnPositions[i % 4].Y +=
ScreenManager.GraphicsDevice.Viewport.Height * 0.03f;
}
}
// draw the instructions
ScreenManager.SpriteBatch.DrawString(ScreenManager.Font, instructions,
new Vector2(ScreenManager.TitleSafeArea.X,
ScreenManager.TitleSafeArea.Y + ScreenManager.TitleSafeArea.Height -
ScreenManager.Font.LineSpacing), Color.White);
ScreenManager.SpriteBatch.End();
base.Draw(gameTime);
}
///
/// When the user cancels the main menu, ask if they want to exit the sample.
///
protected override void OnCancel()
{
if (!IsExiting)
{
ExitScreen();
}
if (world != null)
{
world.Dispose();
world = null;
}
if (networkSession != null)
{
networkSession.Dispose();
networkSession = null;
}
}
#endregion
#region Networking Event Handlers
///
/// Handle the end of the network session.
///
void networkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e)
{
if (!IsExiting)
{
ExitScreen();
}
if (world != null)
{
world.Dispose();
world = null;
}
if (networkSession != null)
{
networkSession.Dispose();
networkSession = null;
}
}
///
/// Handle the start of the game session.
///
void networkSession_GameStarted(object sender, GameStartedEventArgs e)
{
// if we're the host, generate the data
if ((networkSession != null) && networkSession.IsHost && (world != null))
{
world.GenerateWorld();
}
}
///
/// Handle a new player joining the session.
///
void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e)
{
// make sure the data exists for the new gamer
for (int i = 0; i < networkSession.AllGamers.Count; i++)
{
if (networkSession.AllGamers[i] == e.Gamer)
{
PlayerData playerData = new PlayerData();
e.Gamer.Tag = playerData;
playerData.ShipVariation = (byte)(i % 4);
playerData.ShipColor = (byte)i;
}
}
// send my own data to the new gamer
if ((networkSession.LocalGamers.Count > 0) && !e.Gamer.IsLocal)
{
PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData;
if (playerData != null)
{
packetWriter.Write((int)World.PacketTypes.PlayerData);
playerData.Serialize(packetWriter);
networkSession.LocalGamers[0].SendData(packetWriter,
SendDataOptions.ReliableInOrder, e.Gamer);
}
}
}
#endregion
#region IDisposable Implementation
///
/// Finalizes the LobbyScreen object, calls Dispose(false)
///
~LobbyScreen()
{
Dispose(false);
}
///
/// Disposes the LobbyScreen object.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Disposes this object.
///
///
/// True if this method was called as part of the Dispose method.
///
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (this)
{
if (world != null)
{
world.Dispose();
world = null;
}
if (packetWriter != null)
{
packetWriter.Close();
packetWriter = null;
}
}
}
}
#endregion
}
}