| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393 |
- //-----------------------------------------------------------------------------
- // World.cs
- //
- // Microsoft XNA Community Game Platform
- // Copyright (C) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- 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;
- namespace NetRumble
- {
- /// <summary>
- /// A container for the game-specific logic and code.
- /// </summary>
- public class World : IDisposable
- {
- /// <summary>
- /// The maximum number of players in the game.
- /// </summary>
- public const int MaximumPlayers = 16;
- /// <summary>
- /// The different types of packets sent in the game.
- /// </summary>
- /// <remarks>Frequently used in packets to identify their type.</remarks>
- public enum PacketTypes
- {
- PlayerData,
- ShipData,
- WorldSetup,
- WorldData,
- ShipInput,
- PowerUpSpawn,
- ShipDeath,
- ShipSpawn,
- GameWon,
- };
- /// <summary>
- /// The score required to win the game.
- /// </summary>
- const int winningScore = 5;
- /// <summary>
- /// The number of asteroids in the game.
- /// </summary>
- const int numberOfAsteroids = 15;
- /// <summary>
- /// The length of time it takes for another power-up to spawn.
- /// </summary>
- const float maximumPowerUpTimer = 10f;
- /// <summary>
- /// The size of all of the barriers in the game.
- /// </summary>
- const int barrierSize = 48;
- /// <summary>
- /// The number of updates between WorldData packets.
- /// </summary>
- const int updatesBetweenWorldDataSend = 30;
- /// <summary>
- /// The number of updates between ship status packets from this machine.
- /// </summary>
- const int updatesBetweenStatusPackets = MaximumPlayers;
- /// <summary>
- /// The number of barriers in each dimension.
- /// </summary>
- static readonly Point barrierCounts = new Point(50, 50);
- /// <summary>
- /// The dimensions of the game world.
- /// </summary>
- static readonly Rectangle dimensions = new Rectangle(0, 0,
- barrierCounts.X * barrierSize, barrierCounts.Y * barrierSize);
- /// <summary>
- /// If true, the game has been initialized by receiving a WorldSetup packet.
- /// </summary>
- bool initialized = false;
- public bool Initialized
- {
- get { return initialized; }
- }
-
- /// <summary>
- /// If true, the game is over, and somebody has won.
- /// </summary>
- private bool gameWon = false;
- public bool GameWon
- {
- get { return gameWon; }
- set { gameWon = value; }
- }
- /// <summary>
- /// The index of the player who won the game.
- /// </summary>
- private int winnerIndex = -1;
- public int WinnerIndex
- {
- get { return winnerIndex; }
- }
- /// <summary>
- /// If true, the game is over, because the game ended before somebody won.
- /// </summary>
- /// <remarks></remarks>
- private bool gameExited = false;
- public bool GameExited
- {
- get { return gameExited; }
- set { gameExited = value; }
- }
- // presence support
- private List<int> highScorers = new List<int>();
- public List<int> HighScorers
- {
- get { return highScorers; }
- }
- /// <summary>
- /// The number of asteroids in the game.
- /// </summary>
- Asteroid[] asteroids = new Asteroid[numberOfAsteroids];
- /// <summary>
- /// The current power-up in the game.
- /// </summary>
- PowerUp powerUp = null;
- /// <summary>
- /// The amount of time left until the next power-up spawns.
- /// </summary>
- float powerUpTimer = maximumPowerUpTimer / 2f;
- /// <summary>
- /// The sprite batch used to draw the objects in the world.
- /// </summary>
- private SpriteBatch spriteBatch;
- /// <summary>
- /// The corner-barrier texture.
- /// </summary>
- private Texture2D cornerBarrierTexture;
- /// <summary>
- /// The vertical-barrier texture.
- /// </summary>
- private Texture2D verticalBarrierTexture;
- /// <summary>
- /// The horizontal-barrier texture.
- /// </summary>
- private Texture2D horizontalBarrierTexture;
- /// <summary>
- /// The texture signifying that the player can chat.
- /// </summary>
- private Texture2D chatAbleTexture;
- /// <summary>
- /// The texture signifying that the player has been muted.
- /// </summary>
- private Texture2D chatMuteTexture;
- /// <summary>
- /// The texture signifying that the player is talking right now.
- /// </summary>
- private Texture2D chatTalkingTexture;
- /// <summary>
- /// The texture signifying that the player is ready
- /// </summary>
- private Texture2D readyTexture;
- /// <summary>
- /// The sprite used to draw the player names.
- /// </summary>
- private SpriteFont playerFont;
- public SpriteFont PlayerFont
- {
- get { return playerFont; }
- }
-
- /// <summary>
- /// The list of corner barriers in the game world.
- /// </summary>
- /// <remarks>This list is not owned by this object.</remarks>
- private List<Rectangle> cornerBarriers = new List<Rectangle>();
- /// <summary>
- /// The list of vertical barriers in the game world.
- /// </summary>
- /// <remarks>This list is not owned by this object.</remarks>
- private List<Rectangle> verticalBarriers = new List<Rectangle>();
- /// <summary>
- /// The list of horizontal barriers in the game world.
- /// </summary>
- /// <remarks>This list is not owned by this object.</remarks>
- private List<Rectangle> horizontalBarriers = new List<Rectangle>();
- /// <summary>
- /// The particle-effect manager for the game.
- /// </summary>
- ParticleEffectManager particleEffectManager;
- /// <summary>
- /// The network session for the game.
- /// </summary>
- private NetworkSession networkSession;
- /// <summary>
- /// The packet writer for all of the data for the world.
- /// </summary>
- private PacketWriter packetWriter = new PacketWriter();
- /// <summary>
- /// The packet reader for all of the data for the world.
- /// </summary>
- private PacketReader packetReader = new PacketReader();
- /// <summary>
- /// The number of updates that have passed since the world data was sent.
- /// </summary>
- private int updatesSinceWorldDataSend = 0;
- /// <summary>
- /// The number of updates that have passed since a status packet was sent.
- /// </summary>
- private int updatesSinceStatusPacket = 0;
- /// <summary>
- /// Construct a new World object.
- /// </summary>
- /// <param name="graphicsDevice">The graphics device used for this game.</param>
- /// <param name="networkSession">The network session for this game.</param>
- 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<SpriteFont>("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<Texture2D>("Textures/chatAble");
- chatMuteTexture = contentManager.Load<Texture2D>("Textures/chatMute");
- chatTalkingTexture = contentManager.Load<Texture2D>("Textures/chatTalking");
- readyTexture = contentManager.Load<Texture2D>("Textures/ready");
- cornerBarrierTexture =
- contentManager.Load<Texture2D>("Textures/barrierEnd");
- verticalBarrierTexture =
- contentManager.Load<Texture2D>("Textures/barrierPurple");
- horizontalBarrierTexture =
- contentManager.Load<Texture2D>("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
- }
- }
- /// <summary>
- /// Generate the initial state of the game, and send it to everyone.
- /// </summary>
- 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);
- }
- }
- /// <summary>
- /// Reset per-round game state flags (shared by Initialize and host GenerateWorld).
- /// </summary>
- private void ResetGameState()
- {
- gameWon = false;
- winnerIndex = -1;
- gameExited = false;
- }
- /// <summary>
- /// Initialize the world with the data from the WorldSetup packet.
- /// </summary>
- /// <param name="packetReader">The packet reader with the world data.</param>
- public void Initialize()
- {
- // reset the game status
- ResetGameState();
- // 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;
- }
- /// <summary>
- /// Update the world.
- /// </summary>
- /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
- /// <param name="paused">If true, the game is paused.</param>
- 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);
- }
- }
- }
- /// <summary>
- /// Process the local player's input.
- /// </summary>
- private void ProcessLocalPlayerInput()
- {
- if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
- {
- // create the new input structure
- ShipInput shipInput = new ShipInput(
- GamePad.GetState(
- (PlayerIndex)networkSession.LocalGamers[0].SignedInGamer.PlayerIndex),
- Keyboard.GetState());
- // 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);
- }
- }
- /// <summary>
- /// Send the current state of the ship to the other players.
- /// </summary>
- 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);
- }
- }
- }
- /// <summary>
- /// Send a notification of the death of the local ship to the other players.
- /// </summary>
- 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);
- }
- }
- }
- /// <summary>
- /// Process incoming packets on the local gamer.
- /// </summary>
- 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;
- }
- }
- }
- }
- /// <summary>
- /// Spawn a ship based on the data in the packet.
- /// </summary>
- 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();
- }
- }
- }
- /// <summary>
- /// Spawn a power-up based on the data in the packet.
- /// </summary>
- 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();
- }
- }
- /// <summary>
- /// Kill the sender's ship based on data in the packet.
- /// </summary>
- /// <param name="sender">The sender of the packet.</param>
- 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);
- }
- }
- }
- /// <summary>
- /// Update the player data for the sender based on the data in the packet.
- /// </summary>
- /// <param name="sender">The sender of the packet.</param>
- 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);
- }
- }
- }
- }
- }
- /// <summary>
- /// Update ship state based on the data in the packet.
- /// </summary>
- /// <param name="sender">The sender of the packet.</param>
- 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();
- }
- }
- }
- /// <summary>
- /// Update the world data based on the data in the packet.
- /// </summary>
- 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();
- }
- }
- /// <summary>
- /// Draws the objects in the world.
- /// </summary>
- /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
- /// <param name="center">The center of the current view.</param>
- 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();
- }
- /// <summary>
- /// Draw the specified player's data in the screen - gamertag, etc.
- /// </summary>
- /// <param name="totalTime">The total time spent in the game.</param>
- /// <param name="networkGamer">The player to be drawn.</param>
- /// <param name="position">The center of the desired location.</param>
- /// <param name="spriteBatch">The SpriteBatch object used to draw.</param>
- /// <param name="lobby">If true, drawn "lobby style"</param>
- 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);
- }
- }
- }
- /// <summary>
- /// Finalizes the World object, calls Dispose(false)
- /// </summary>
- ~World()
- {
- Dispose(false);
- }
- /// <summary>
- /// Disposes the World object.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Disposes this object.
- /// </summary>
- /// <param name="disposing">
- /// True if this method was called as part of the Dispose method.
- /// </param>
- 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);
- }
- }
- }
- }
- }
|