#region File Description //----------------------------------------------------------------------------- // World.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Net; #endregion namespace NetRumble { /// /// A container for the game-specific logic and code. /// public class World : IDisposable { #region Public Constants /// /// The maximum number of players in the game. /// public const int MaximumPlayers = 16; /// /// The different types of packets sent in the game. /// /// Frequently used in packets to identify their type. public enum PacketTypes { PlayerData, ShipData, WorldSetup, WorldData, ShipInput, PowerUpSpawn, ShipDeath, ShipSpawn, GameWon, }; #endregion #region Constants /// /// The score required to win the game. /// const int winningScore = 5; /// /// The number of asteroids in the game. /// const int numberOfAsteroids = 15; /// /// The length of time it takes for another power-up to spawn. /// const float maximumPowerUpTimer = 10f; /// /// The size of all of the barriers in the game. /// const int barrierSize = 48; /// /// The number of updates between WorldData packets. /// const int updatesBetweenWorldDataSend = 30; /// /// The number of updates between ship status packets from this machine. /// const int updatesBetweenStatusPackets = MaximumPlayers; /// /// The number of barriers in each dimension. /// static readonly Point barrierCounts = new Point(50, 50); /// /// The dimensions of the game world. /// static readonly Rectangle dimensions = new Rectangle(0, 0, barrierCounts.X * barrierSize, barrierCounts.Y * barrierSize); #endregion #region State Data /// /// If true, the game has been initialized by receiving a WorldSetup packet. /// bool initialized = false; public bool Initialized { get { return initialized; } } /// /// If true, the game is over, and somebody has won. /// private bool gameWon = false; public bool GameWon { get { return gameWon; } set { gameWon = value; } } /// /// The index of the player who won the game. /// private int winnerIndex = -1; public int WinnerIndex { get { return winnerIndex; } } /// /// If true, the game is over, because the game ended before somebody won. /// /// private bool gameExited = false; public bool GameExited { get { return gameExited; } set { gameExited = value; } } // presence support private List highScorers = new List(); public List HighScorers { get { return highScorers; } } #endregion #region Gameplay Data /// /// The number of asteroids in the game. /// Asteroid[] asteroids = new Asteroid[numberOfAsteroids]; /// /// The current power-up in the game. /// PowerUp powerUp = null; /// /// The amount of time left until the next power-up spawns. /// float powerUpTimer = maximumPowerUpTimer / 2f; #endregion #region Graphics Data /// /// The sprite batch used to draw the objects in the world. /// private SpriteBatch spriteBatch; /// /// The corner-barrier texture. /// private Texture2D cornerBarrierTexture; /// /// The vertical-barrier texture. /// private Texture2D verticalBarrierTexture; /// /// The horizontal-barrier texture. /// private Texture2D horizontalBarrierTexture; /// /// The texture signifying that the player can chat. /// private Texture2D chatAbleTexture; /// /// The texture signifying that the player has been muted. /// private Texture2D chatMuteTexture; /// /// The texture signifying that the player is talking right now. /// private Texture2D chatTalkingTexture; /// /// The texture signifying that the player is ready /// private Texture2D readyTexture; /// /// The sprite used to draw the player names. /// private SpriteFont playerFont; public SpriteFont PlayerFont { get { return playerFont; } } /// /// The list of corner barriers in the game world. /// /// This list is not owned by this object. private List cornerBarriers = new List(); /// /// The list of vertical barriers in the game world. /// /// This list is not owned by this object. private List verticalBarriers = new List(); /// /// The list of horizontal barriers in the game world. /// /// This list is not owned by this object. private List horizontalBarriers = new List(); /// /// The particle-effect manager for the game. /// ParticleEffectManager particleEffectManager; #endregion #region Networking Data /// /// The network session for the game. /// private NetworkSession networkSession; /// /// The packet writer for all of the data for the world. /// private PacketWriter packetWriter = new PacketWriter(); /// /// The packet reader for all of the data for the world. /// private PacketReader packetReader = new PacketReader(); /// /// The number of updates that have passed since the world data was sent. /// private int updatesSinceWorldDataSend = 0; /// /// The number of updates that have passed since a status packet was sent. /// private int updatesSinceStatusPacket = 0; #endregion #region Initialization /// /// Construct a new World object. /// /// The graphics device used for this game. /// The network session for this game. public World(GraphicsDevice graphicsDevice, ContentManager contentManager, NetworkSession networkSession) { // safety-check the parameters, as they must be valid if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (contentManager == null) { throw new ArgumentNullException("contentManager"); } if (networkSession == null) { throw new ArgumentNullException("networkSession"); } // apply the parameter values this.networkSession = networkSession; // set up the staggered status packet system // -- your first update happens based on where you are in the collection for (int i = 0; i < networkSession.AllGamers.Count; i++) { if (networkSession.AllGamers[i].IsLocal) { updatesSinceStatusPacket = i; break; } } // create the spritebatch spriteBatch = new SpriteBatch(graphicsDevice); // create and initialize the particle-effect manager particleEffectManager = new ParticleEffectManager(contentManager); particleEffectManager.RegisterParticleEffect( ParticleEffectType.LaserExplosion, "Particles/laserExplosion.xml", 40); particleEffectManager.RegisterParticleEffect( ParticleEffectType.MineExplosion, "Particles/mineExplosion.xml", 8); particleEffectManager.RegisterParticleEffect( ParticleEffectType.RocketExplosion, "Particles/rocketExplosion.xml", 24); particleEffectManager.RegisterParticleEffect( ParticleEffectType.RocketTrail, "Particles/rocketTrail.xml", 16); particleEffectManager.RegisterParticleEffect( ParticleEffectType.ShipExplosion, "Particles/shipExplosion.xml", 4); particleEffectManager.RegisterParticleEffect( ParticleEffectType.ShipSpawn, "Particles/shipSpawn.xml", 4); Ship.ParticleEffectManager = particleEffectManager; RocketProjectile.ParticleEffectManager = particleEffectManager; MineProjectile.ParticleEffectManager = particleEffectManager; LaserProjectile.ParticleEffectManager = particleEffectManager; // load the font playerFont = contentManager.Load("Fonts/NetRumbleFont"); // load the gameplay-object textures Ship.LoadContent(contentManager); Asteroid.LoadContent(contentManager); LaserProjectile.LoadContent(contentManager); MineProjectile.LoadContent(contentManager); RocketProjectile.LoadContent(contentManager); DoubleLaserPowerUp.LoadContent(contentManager); TripleLaserPowerUp.LoadContent(contentManager); RocketPowerUp.LoadContent(contentManager); // load the non-gameplay-object textures chatAbleTexture = contentManager.Load("Textures/chatAble"); chatMuteTexture = contentManager.Load("Textures/chatMute"); chatTalkingTexture = contentManager.Load("Textures/chatTalking"); readyTexture = contentManager.Load("Textures/ready"); cornerBarrierTexture = contentManager.Load("Textures/barrierEnd"); verticalBarrierTexture = contentManager.Load("Textures/barrierPurple"); horizontalBarrierTexture = contentManager.Load("Textures/barrierRed"); // clear the collision manager CollisionManager.Collection.Clear(); // add the collision version of the edge barriers CollisionManager.Barriers.Clear(); CollisionManager.Barriers.Add(new Rectangle(dimensions.X, dimensions.Y, dimensions.Width, barrierSize)); // top edge CollisionManager.Barriers.Add(new Rectangle( dimensions.X, dimensions.Y + dimensions.Height, dimensions.Width, barrierSize)); // bottom edge CollisionManager.Barriers.Add(new Rectangle(dimensions.X, dimensions.Y, barrierSize, dimensions.Height)); // left edge CollisionManager.Barriers.Add(new Rectangle( dimensions.X + dimensions.Width, dimensions.Y, barrierSize, dimensions.Height)); // right edge // add the rendering version of the edge barriers cornerBarriers.Clear(); cornerBarriers.Add(new Rectangle(dimensions.X, dimensions.Y, barrierSize, barrierSize)); // top-left corner cornerBarriers.Add(new Rectangle( dimensions.X + dimensions.Width, dimensions.Y, barrierSize, barrierSize)); // top-right corner cornerBarriers.Add(new Rectangle( dimensions.X, dimensions.Y + dimensions.Height, barrierSize, barrierSize)); // bottom-left corner cornerBarriers.Add(new Rectangle( dimensions.X + dimensions.Width, dimensions.Y + dimensions.Height, barrierSize, barrierSize)); // bottom-right corner verticalBarriers.Clear(); for (int i = 1; i < barrierCounts.Y; i++) { verticalBarriers.Add(new Rectangle( dimensions.X, dimensions.Y + barrierSize * i, barrierSize, barrierSize)); // top edge verticalBarriers.Add(new Rectangle( dimensions.X + dimensions.Width, dimensions.Y + barrierSize * i, barrierSize, barrierSize)); // bottom edge } horizontalBarriers.Clear(); for (int i = 1; i < barrierCounts.X; i++) { horizontalBarriers.Add(new Rectangle( dimensions.X + barrierSize * i, dimensions.Y, barrierSize, barrierSize)); // left edge horizontalBarriers.Add(new Rectangle( dimensions.X + barrierSize * i, dimensions.Y + dimensions.Width, barrierSize, barrierSize)); // right edge } } /// /// Generate the initial state of the game, and send it to everyone. /// public void GenerateWorld() { if ((networkSession != null) && (networkSession.LocalGamers.Count > 0)) { // write the identification value packetWriter.Write((int)PacketTypes.WorldSetup); // place the ships // -- we always write the maximum number of players, making the packet // predictable, in case the player count changes on the client before // this packet is received for (int i = 0; i < MaximumPlayers; i++) { Vector2 position = Vector2.Zero; if (i < networkSession.AllGamers.Count) { PlayerData playerData = networkSession.AllGamers[i].Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { playerData.Ship.Initialize(); position = playerData.Ship.Position = CollisionManager.FindSpawnPoint(playerData.Ship, playerData.Ship.Radius * 5f); playerData.Ship.Score = 0; } } // write the ship position packetWriter.Write(position); } // place the asteroids // -- for simplicity, the same number of asteroids is always the same for (int i = 0; i < asteroids.Length; i++) { // choose one of three radii float radius = 32f; switch (RandomMath.Random.Next(3)) { case 0: radius = 32f; break; case 1: radius = 60f; break; case 2: radius = 96f; break; } // create the asteroid asteroids[i] = new Asteroid(radius); // write the radius packetWriter.Write(asteroids[i].Radius); // choose a variation asteroids[i].Variation = i % Asteroid.Variations; // write the variation packetWriter.Write(asteroids[i].Variation); // initialize the asteroid and it's starting position asteroids[i].Initialize(); asteroids[i].Position = CollisionManager.FindSpawnPoint(asteroids[i], asteroids[i].Radius); // write the starting position and velocity packetWriter.Write(asteroids[i].Position); packetWriter.Write(asteroids[i].Velocity); } // send the packet to everyone networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.ReliableInOrder); } } /// /// Initialize the world with the data from the WorldSetup packet. /// /// The packet reader with the world data. public void Initialize() { // reset the game status gameWon = false; winnerIndex = -1; gameExited = false; // initialize the ships with the data from the packet for (int i = 0; i < MaximumPlayers; i++) { // read each of the positions Vector2 position = packetReader.ReadVector2(); // use the position value if we know of that many players if (i < networkSession.AllGamers.Count) { PlayerData playerData = networkSession.AllGamers[i].Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { // initialize the ship with the provided position playerData.Ship.Position = position; playerData.Ship.Score = 0; playerData.Ship.Initialize(); } } } // initialize the ships with the data from the packet for (int i = 0; i < asteroids.Length; i++) { float radius = packetReader.ReadSingle(); if (asteroids[i] == null) { asteroids[i] = new Asteroid(radius); } asteroids[i].Variation = packetReader.ReadInt32(); asteroids[i].Position = packetReader.ReadVector2(); asteroids[i].Initialize(); asteroids[i].Velocity = packetReader.ReadVector2(); } // set the initialized state initialized = true; } #endregion #region Updating Methods /// /// Update the world. /// /// The amount of elapsed time, in seconds. /// If true, the game is paused. public void Update(float elapsedTime, bool paused) { if (gameWon) { // update the particle-effect manager particleEffectManager.Update(elapsedTime); // make sure the collision manager is empty CollisionManager.Collection.ApplyPendingRemovals(); if (CollisionManager.Collection.Count > 0) { CollisionManager.Collection.Clear(); } } else { // process all incoming packets ProcessPackets(); // if the game is in progress, update the state of it if (initialized && (networkSession != null) && (networkSession.SessionState == NetworkSessionState.Playing)) { // presence support int highScore = int.MinValue; int highScoreIndex = -1; for (int i = 0; i < networkSession.AllGamers.Count; i++) { NetworkGamer networkGamer = networkSession.AllGamers[i]; PlayerData playerData = networkGamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { int playerScore = playerData.Ship.Score; if (playerScore == highScore) { highScorers.Add(i); } else if (playerScore > highScore) { highScorers.Clear(); highScorers.Add(i); highScore = playerScore; highScoreIndex = i; } } } // the host has singular responsibilities to the game world, // that need to be done once, by one authority if (networkSession.IsHost) { // get the local player, for frequent re-use LocalNetworkGamer localGamer = networkSession.Host as LocalNetworkGamer; // check for victory // if victory has been achieved, send a packet to everyone if (highScore >= winningScore) { packetWriter.Write((int)PacketTypes.GameWon); packetWriter.Write(highScoreIndex); localGamer.SendData(packetWriter, SendDataOptions.ReliableInOrder); } // respawn each player, if it is time to do so for (int i = 0; i < networkSession.AllGamers.Count; i++) { NetworkGamer networkGamer = networkSession.AllGamers[i]; PlayerData playerData = networkGamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null) && !playerData.Ship.Active && (playerData.Ship.RespawnTimer <= 0f)) { // write the ship-spawn packet packetWriter.Write((int)PacketTypes.ShipSpawn); packetWriter.Write(i); packetWriter.Write(CollisionManager.FindSpawnPoint( playerData.Ship, playerData.Ship.Radius)); localGamer.SendData(packetWriter, SendDataOptions.ReliableInOrder); } } // respawn the power-up if it is time to do so if (powerUp == null) { powerUpTimer -= elapsedTime; if (powerUpTimer < 0) { // write the power-up-spawn packet packetWriter.Write((int)PacketTypes.PowerUpSpawn); packetWriter.Write(RandomMath.Random.Next(3)); packetWriter.Write(CollisionManager.FindSpawnPoint(null, PowerUp.PowerUpRadius * 3f)); localGamer.SendData(packetWriter, SendDataOptions.ReliableInOrder); } } else { powerUpTimer = maximumPowerUpTimer; } // send everyone an update on the state of the world if (updatesSinceWorldDataSend >= updatesBetweenWorldDataSend) { packetWriter.Write((int)PacketTypes.WorldData); // write each of the asteroids for (int i = 0; i < asteroids.Length; i++) { packetWriter.Write(asteroids[i].Position); packetWriter.Write(asteroids[i].Velocity); } localGamer.SendData(packetWriter, SendDataOptions.InOrder); updatesSinceWorldDataSend = 0; } else { updatesSinceWorldDataSend++; } } // update each asteroid foreach (Asteroid asteroid in asteroids) { if (asteroid.Active) { asteroid.Update(elapsedTime); } } // update the power-up if (powerUp != null) { if (powerUp.Active) { powerUp.Update(elapsedTime); } else { powerUp = null; } } // process the local player's input if (!paused) { ProcessLocalPlayerInput(); } // update each ship foreach (NetworkGamer networkGamer in networkSession.AllGamers) { PlayerData playerData = networkGamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { if (playerData.Ship.Active) { playerData.Ship.Update(elapsedTime); // check for death // -- only check on local machines - the local player is // the authority on the death of their own ship if (networkGamer.IsLocal && (playerData.Ship.Life < 0)) { SendLocalShipDeath(); } } else if (playerData.Ship.RespawnTimer > 0f) { playerData.Ship.RespawnTimer -= elapsedTime; if (playerData.Ship.RespawnTimer < 0f) { playerData.Ship.RespawnTimer = 0f; } } } } // update the other players with the current state of the local ship if (updatesSinceStatusPacket >= updatesBetweenStatusPackets) { updatesSinceStatusPacket = 0; SendLocalShipData(); } else { updatesSinceStatusPacket++; } // update the collision manager CollisionManager.Update(elapsedTime); // update the particle-effect manager particleEffectManager.Update(elapsedTime); } } } /// /// Process the local player's input. /// private void ProcessLocalPlayerInput() { if ((networkSession != null) && (networkSession.LocalGamers.Count > 0)) { // create the new input structure ShipInput shipInput = new ShipInput( GamePad.GetState( networkSession.LocalGamers[0].SignedInGamer.PlayerIndex), Keyboard.GetState( networkSession.LocalGamers[0].SignedInGamer.PlayerIndex)); // send it out // -- the local machine will receive and apply it from the network just // like the other clients shipInput.Serialize(packetWriter); networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.InOrder); } } /// /// Send the current state of the ship to the other players. /// private void SendLocalShipData() { if ((networkSession != null) && (networkSession.LocalGamers.Count > 0)) { PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { packetWriter.Write((int)World.PacketTypes.ShipData); packetWriter.Write(playerData.Ship.Position); packetWriter.Write(playerData.Ship.Velocity); packetWriter.Write(playerData.Ship.Rotation); packetWriter.Write(playerData.Ship.Life); packetWriter.Write(playerData.Ship.Shield); packetWriter.Write(playerData.Ship.Score); networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.InOrder); } } } /// /// Send a notification of the death of the local ship to the other players. /// private void SendLocalShipDeath() { if ((networkSession != null) && (networkSession.LocalGamers.Count > 0)) { LocalNetworkGamer localNetworkGamer = networkSession.LocalGamers[0] as LocalNetworkGamer; PlayerData playerData = localNetworkGamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { // send a ship-death notification packetWriter.Write((int)PacketTypes.ShipDeath); // determine the player behind the last damage taken int lastDamagedByPlayer = -1; Ship lastDamagedByShip = playerData.Ship.LastDamagedBy as Ship; if ((lastDamagedByShip != null) && (lastDamagedByShip != playerData.Ship)) { for (int i = 0; i < networkSession.AllGamers.Count; i++) { PlayerData sourcePlayerData = networkSession.AllGamers[i].Tag as PlayerData; if ((sourcePlayerData != null) && (sourcePlayerData.Ship != null) && (sourcePlayerData.Ship == lastDamagedByShip)) { lastDamagedByPlayer = i; break; } } } packetWriter.Write(lastDamagedByPlayer); localNetworkGamer.SendData(packetWriter, SendDataOptions.ReliableInOrder); } } } #endregion #region Packet Handling Methods /// /// Process incoming packets on the local gamer. /// private void ProcessPackets() { if ((networkSession != null) && (networkSession.LocalGamers.Count > 0)) { // process all packets found, every frame while (networkSession.LocalGamers[0].IsDataAvailable) { NetworkGamer sender; networkSession.LocalGamers[0].ReceiveData(packetReader, out sender); // read the type of packet... PacketTypes packetType = (PacketTypes)packetReader.ReadInt32(); // ... and dispatch appropriately switch (packetType) { case PacketTypes.PlayerData: UpdatePlayerData(sender); break; case PacketTypes.WorldSetup: // apply the world setup data, but only once if (!Initialized) { Initialize(); } break; case PacketTypes.ShipData: if ((sender != null) && !sender.IsLocal) { UpdateShipData(sender); } break; case PacketTypes.WorldData: if (!networkSession.IsHost && Initialized) { UpdateWorldData(); } break; case PacketTypes.ShipInput: if (sender != null) { PlayerData playerData = sender.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { playerData.Ship.ShipInput = new ShipInput(packetReader); } } break; case PacketTypes.ShipSpawn: SpawnShip(); break; case PacketTypes.PowerUpSpawn: SpawnPowerup(); break; case PacketTypes.ShipDeath: KillShip(sender); break; case PacketTypes.GameWon: gameWon = true; winnerIndex = packetReader.ReadInt32(); if (networkSession.IsHost && (networkSession.SessionState == NetworkSessionState.Playing)) { networkSession.EndGame(); } break; } } } } /// /// Spawn a ship based on the data in the packet. /// private void SpawnShip() { int whichGamer = packetReader.ReadInt32(); if (whichGamer < networkSession.AllGamers.Count) { NetworkGamer networkGamer = networkSession.AllGamers[whichGamer]; PlayerData playerData = networkGamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { playerData.Ship.Position = packetReader.ReadVector2(); playerData.Ship.Initialize(); } } } /// /// Spawn a power-up based on the data in the packet. /// private void SpawnPowerup() { int whichPowerUp = packetReader.ReadInt32(); if (powerUp == null) { switch (whichPowerUp) { case 0: powerUp = new DoubleLaserPowerUp(); break; case 1: powerUp = new TripleLaserPowerUp(); break; case 2: powerUp = new RocketPowerUp(); break; } } if (powerUp != null) { powerUp.Position = packetReader.ReadVector2(); powerUp.Initialize(); } } /// /// Kill the sender's ship based on data in the packet. /// /// The sender of the packet. private void KillShip(NetworkGamer sender) { if (sender != null) { PlayerData playerData = sender.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null) && playerData.Ship.Active) { GameplayObject source = null; // read the index of the source of the last damage taken int sourcePlayerIndex = packetReader.ReadInt32(); if ((sourcePlayerIndex >= 0) && (sourcePlayerIndex < networkSession.AllGamers.Count)) { PlayerData sourcePlayerData = networkSession.AllGamers[sourcePlayerIndex].Tag as PlayerData; source = sourcePlayerData != null ? sourcePlayerData.Ship : null; } // kill the ship playerData.Ship.Die(source, false); } } } /// /// Update the player data for the sender based on the data in the packet. /// /// The sender of the packet. private void UpdatePlayerData(NetworkGamer sender) { if ((networkSession != null) && (networkSession.LocalGamers.Count > 0) && (sender != null)) { PlayerData playerData = sender.Tag as PlayerData; if (playerData != null) { playerData.Deserialize(packetReader); // see if we're still unique // -- this can happen legitimately as we receive introductory data foreach (LocalNetworkGamer localNetworkGamer in networkSession.LocalGamers) { PlayerData localPlayerData = localNetworkGamer.Tag as PlayerData; if ((localPlayerData != null) && !Ship.HasUniqueColorIndex(localNetworkGamer, networkSession)) { localPlayerData.ShipColor = Ship.GetNextUniqueColorIndex( localPlayerData.ShipColor, networkSession); packetWriter.Write((int)World.PacketTypes.PlayerData); localPlayerData.Serialize(packetWriter); networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.ReliableInOrder); } } } } } /// /// Update ship state based on the data in the packet. /// /// The sender of the packet. private void UpdateShipData(NetworkGamer sender) { if (sender != null) { PlayerData playerData = sender.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { playerData.Ship.Position = packetReader.ReadVector2(); playerData.Ship.Velocity = packetReader.ReadVector2(); playerData.Ship.Rotation = packetReader.ReadSingle(); playerData.Ship.Life = packetReader.ReadSingle(); playerData.Ship.Shield = packetReader.ReadSingle(); playerData.Ship.Score = packetReader.ReadInt32(); } } } /// /// Update the world data based on the data in the packet. /// private void UpdateWorldData() { // safety-check the parameters, as they must be valid if (packetReader == null) { throw new ArgumentNullException("packetReader"); } for (int i = 0; i < asteroids.Length; i++) { asteroids[i].Position = packetReader.ReadVector2(); asteroids[i].Velocity = packetReader.ReadVector2(); } } #endregion #region Drawing Methods /// /// Draws the objects in the world. /// /// The amount of elapsed time, in seconds. /// The center of the current view. public void Draw(float elapsedTime, Vector2 center) { Matrix transform = Matrix.CreateTranslation( new Vector3(-center.X, -center.Y, 0f)); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, null, transform); // draw the barriers foreach (Rectangle rectangle in cornerBarriers) { spriteBatch.Draw(cornerBarrierTexture, rectangle, Color.White); } foreach (Rectangle rectangle in verticalBarriers) { spriteBatch.Draw(verticalBarrierTexture, rectangle, Color.White); } foreach (Rectangle rectangle in horizontalBarriers) { spriteBatch.Draw(horizontalBarrierTexture, rectangle, Color.White); } // draw the asteroids foreach (Asteroid asteroid in asteroids) { if (asteroid.Active) { asteroid.Draw(elapsedTime, spriteBatch); } } // draw the powerup if ((powerUp != null) && powerUp.Active) { powerUp.Draw(elapsedTime, spriteBatch); } // draw the ships foreach (NetworkGamer networkGamer in networkSession.AllGamers) { PlayerData playerData = networkGamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null) && playerData.Ship.Active) { playerData.Ship.Draw(elapsedTime, spriteBatch); } } // draw the alpha-blended particles particleEffectManager.Draw(spriteBatch, SpriteBlendMode.AlphaBlend); spriteBatch.End(); // draw the additive particles spriteBatch.Begin(SpriteSortMode.Texture, BlendState.Additive, null, null, null, null, transform); particleEffectManager.Draw(spriteBatch, SpriteBlendMode.Additive); spriteBatch.End(); } /// /// Draw the specified player's data in the screen - gamertag, etc. /// /// The total time spent in the game. /// The player to be drawn. /// The center of the desired location. /// The SpriteBatch object used to draw. /// If true, drawn "lobby style" public void DrawPlayerData(float totalTime, NetworkGamer networkGamer, Vector2 position, SpriteBatch spriteBatch, bool lobby) { // safety-check the parameters, as they must be valid if (networkGamer == null) { throw new ArgumentNullException("networkGamer"); } if (spriteBatch == null) { throw new ArgumentNullException("spriteBatch"); } // get the player data PlayerData playerData = networkGamer.Tag as PlayerData; if (playerData == null) { return; } // draw the gamertag float playerStringScale = 1.0f; if (networkGamer.IsLocal) { // pulse the scale of local gamers playerStringScale = 1f + 0.08f * (1f + (float)Math.Sin(totalTime * 4f)); } string playerString = networkGamer.Gamertag; Color playerColor = playerData.Ship == null ? Ship.ShipColors[playerData.ShipColor] : playerData.Ship.Color; Vector2 playerStringSize = playerFont.MeasureString(playerString); Vector2 playerStringPosition = position; spriteBatch.DrawString(playerFont, playerString, playerStringPosition, playerColor, 0f, new Vector2(playerStringSize.X / 2f, playerStringSize.Y / 2f), playerStringScale, SpriteEffects.None, 0f); // draw the chat texture Texture2D chatTexture = null; if (networkGamer.IsMutedByLocalUser) { chatTexture = chatMuteTexture; } else if (networkGamer.IsTalking) { chatTexture = chatTalkingTexture; } else if (networkGamer.HasVoice) { chatTexture = chatAbleTexture; } if (chatTexture != null) { float chatTextureScale = 0.9f * playerStringSize.Y / (float)chatTexture.Height; Vector2 chatTexturePosition = new Vector2(playerStringPosition.X - 1.2f * playerStringSize.X / 2f - 1.1f * chatTextureScale * (float)chatTexture.Width / 2f, playerStringPosition.Y); spriteBatch.Draw(chatTexture, chatTexturePosition, null, Color.White, 0f, new Vector2((float)chatTexture.Width / 2f, (float)chatTexture.Height / 2f), chatTextureScale, SpriteEffects.None, 0f); } // if we're in "lobby mode", draw a sample version of the ship, // and the ready texture if (lobby) { // draw the ship if (playerData.Ship != null) { float oldShipShield = playerData.Ship.Shield; float oldShipRadius = playerData.Ship.Radius; Vector2 oldShipPosition = playerData.Ship.Position; float oldShipRotation = playerData.Ship.Rotation; playerData.Ship.Shield = 0f; playerData.Ship.Radius = 0.6f * (float)playerStringSize.Y; playerData.Ship.Position = new Vector2(playerStringPosition.X + 1.2f * playerStringSize.X / 2f + 1.1f * playerData.Ship.Radius, playerStringPosition.Y); playerData.Ship.Rotation = 0f; playerData.Ship.Draw(0f, spriteBatch); playerData.Ship.Rotation = oldShipRotation; playerData.Ship.Position = oldShipPosition; playerData.Ship.Shield = oldShipShield; playerData.Ship.Radius = oldShipRadius; } // draw the ready texture if ((readyTexture != null) && networkGamer.IsReady) { float readyTextureScale = 0.9f * playerStringSize.Y / (float)readyTexture.Height; Vector2 readyTexturePosition = new Vector2(playerStringPosition.X + 1.2f * playerStringSize.X / 2f + 2.2f * playerData.Ship.Radius + 1.1f * readyTextureScale * (float)readyTexture.Width / 2f, playerStringPosition.Y); spriteBatch.Draw(readyTexture, readyTexturePosition, null, Color.White, 0f, new Vector2((float)readyTexture.Width / 2f, (float)readyTexture.Height / 2f), readyTextureScale, SpriteEffects.None, 0f); } } else { // if we're not in "lobby mode", draw the score if (playerData.Ship != null) { string scoreString = String.Empty; if (playerData.Ship.Active) { scoreString = playerData.Ship.Score.ToString(); } else { int respawnTimer = (int)Math.Ceiling(playerData.Ship.RespawnTimer); scoreString = "Respawning in: " + respawnTimer.ToString(); } Vector2 scoreStringSize = playerFont.MeasureString(scoreString); Vector2 scoreStringPosition = new Vector2(position.X, position.Y + 0.9f * playerStringSize.Y); spriteBatch.DrawString(playerFont, scoreString, scoreStringPosition, playerColor, 0f, new Vector2(scoreStringSize.X / 2f, scoreStringSize.Y / 2f), 1f, SpriteEffects.None, 0f); } } } #endregion #region IDisposable Implementation /// /// Finalizes the World object, calls Dispose(false) /// ~World() { Dispose(false); } /// /// Disposes the World 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 (packetReader != null) { packetReader.Close(); packetReader = null; } if (packetWriter != null) { packetWriter.Close(); packetWriter = null; } if (spriteBatch != null) { spriteBatch.Dispose(); spriteBatch = null; } cornerBarrierTexture = null; verticalBarrierTexture = null; horizontalBarrierTexture = null; Ship.UnloadContent(); Asteroid.UnloadContent(); LaserProjectile.UnloadContent(); MineProjectile.UnloadContent(); RocketProjectile.UnloadContent(); DoubleLaserPowerUp.UnloadContent(); TripleLaserPowerUp.UnloadContent(); Ship.ParticleEffectManager = null; RocketProjectile.ParticleEffectManager = null; MineProjectile.ParticleEffectManager = null; LaserProjectile.ParticleEffectManager = null; particleEffectManager.UnregisterParticleEffect( ParticleEffectType.MineExplosion); particleEffectManager.UnregisterParticleEffect( ParticleEffectType.RocketExplosion); particleEffectManager.UnregisterParticleEffect( ParticleEffectType.RocketTrail); particleEffectManager.UnregisterParticleEffect( ParticleEffectType.ShipExplosion); particleEffectManager.UnregisterParticleEffect( ParticleEffectType.ShipSpawn); } } } #endregion } }