World.cs 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. //-----------------------------------------------------------------------------
  2. // World.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using Microsoft.Xna.Framework;
  10. using Microsoft.Xna.Framework.Content;
  11. using Microsoft.Xna.Framework.GamerServices;
  12. using Microsoft.Xna.Framework.Graphics;
  13. using Microsoft.Xna.Framework.Input;
  14. using Microsoft.Xna.Framework.Net;
  15. namespace NetRumble
  16. {
  17. /// <summary>
  18. /// A container for the game-specific logic and code.
  19. /// </summary>
  20. public class World : IDisposable
  21. {
  22. /// <summary>
  23. /// The maximum number of players in the game.
  24. /// </summary>
  25. public const int MaximumPlayers = 16;
  26. /// <summary>
  27. /// The different types of packets sent in the game.
  28. /// </summary>
  29. /// <remarks>Frequently used in packets to identify their type.</remarks>
  30. public enum PacketTypes
  31. {
  32. PlayerData,
  33. ShipData,
  34. WorldSetup,
  35. WorldData,
  36. ShipInput,
  37. PowerUpSpawn,
  38. ShipDeath,
  39. ShipSpawn,
  40. GameWon,
  41. };
  42. /// <summary>
  43. /// The score required to win the game.
  44. /// </summary>
  45. const int winningScore = 5;
  46. /// <summary>
  47. /// The number of asteroids in the game.
  48. /// </summary>
  49. const int numberOfAsteroids = 15;
  50. /// <summary>
  51. /// The length of time it takes for another power-up to spawn.
  52. /// </summary>
  53. const float maximumPowerUpTimer = 10f;
  54. /// <summary>
  55. /// The size of all of the barriers in the game.
  56. /// </summary>
  57. const int barrierSize = 48;
  58. /// <summary>
  59. /// The number of updates between WorldData packets.
  60. /// </summary>
  61. const int updatesBetweenWorldDataSend = 30;
  62. /// <summary>
  63. /// The number of updates between ship status packets from this machine.
  64. /// </summary>
  65. const int updatesBetweenStatusPackets = MaximumPlayers;
  66. /// <summary>
  67. /// The number of barriers in each dimension.
  68. /// </summary>
  69. static readonly Point barrierCounts = new Point(50, 50);
  70. /// <summary>
  71. /// The dimensions of the game world.
  72. /// </summary>
  73. static readonly Rectangle dimensions = new Rectangle(0, 0,
  74. barrierCounts.X * barrierSize, barrierCounts.Y * barrierSize);
  75. /// <summary>
  76. /// If true, the game has been initialized by receiving a WorldSetup packet.
  77. /// </summary>
  78. bool initialized = false;
  79. public bool Initialized
  80. {
  81. get { return initialized; }
  82. }
  83. /// <summary>
  84. /// If true, the game is over, and somebody has won.
  85. /// </summary>
  86. private bool gameWon = false;
  87. public bool GameWon
  88. {
  89. get { return gameWon; }
  90. set { gameWon = value; }
  91. }
  92. /// <summary>
  93. /// The index of the player who won the game.
  94. /// </summary>
  95. private int winnerIndex = -1;
  96. public int WinnerIndex
  97. {
  98. get { return winnerIndex; }
  99. }
  100. /// <summary>
  101. /// If true, the game is over, because the game ended before somebody won.
  102. /// </summary>
  103. /// <remarks></remarks>
  104. private bool gameExited = false;
  105. public bool GameExited
  106. {
  107. get { return gameExited; }
  108. set { gameExited = value; }
  109. }
  110. // presence support
  111. private List<int> highScorers = new List<int>();
  112. public List<int> HighScorers
  113. {
  114. get { return highScorers; }
  115. }
  116. /// <summary>
  117. /// The number of asteroids in the game.
  118. /// </summary>
  119. Asteroid[] asteroids = new Asteroid[numberOfAsteroids];
  120. /// <summary>
  121. /// The current power-up in the game.
  122. /// </summary>
  123. PowerUp powerUp = null;
  124. /// <summary>
  125. /// The amount of time left until the next power-up spawns.
  126. /// </summary>
  127. float powerUpTimer = maximumPowerUpTimer / 2f;
  128. /// <summary>
  129. /// The sprite batch used to draw the objects in the world.
  130. /// </summary>
  131. private SpriteBatch spriteBatch;
  132. /// <summary>
  133. /// The corner-barrier texture.
  134. /// </summary>
  135. private Texture2D cornerBarrierTexture;
  136. /// <summary>
  137. /// The vertical-barrier texture.
  138. /// </summary>
  139. private Texture2D verticalBarrierTexture;
  140. /// <summary>
  141. /// The horizontal-barrier texture.
  142. /// </summary>
  143. private Texture2D horizontalBarrierTexture;
  144. /// <summary>
  145. /// The texture signifying that the player can chat.
  146. /// </summary>
  147. private Texture2D chatAbleTexture;
  148. /// <summary>
  149. /// The texture signifying that the player has been muted.
  150. /// </summary>
  151. private Texture2D chatMuteTexture;
  152. /// <summary>
  153. /// The texture signifying that the player is talking right now.
  154. /// </summary>
  155. private Texture2D chatTalkingTexture;
  156. /// <summary>
  157. /// The texture signifying that the player is ready
  158. /// </summary>
  159. private Texture2D readyTexture;
  160. /// <summary>
  161. /// The sprite used to draw the player names.
  162. /// </summary>
  163. private SpriteFont playerFont;
  164. public SpriteFont PlayerFont
  165. {
  166. get { return playerFont; }
  167. }
  168. /// <summary>
  169. /// The list of corner barriers in the game world.
  170. /// </summary>
  171. /// <remarks>This list is not owned by this object.</remarks>
  172. private List<Rectangle> cornerBarriers = new List<Rectangle>();
  173. /// <summary>
  174. /// The list of vertical barriers in the game world.
  175. /// </summary>
  176. /// <remarks>This list is not owned by this object.</remarks>
  177. private List<Rectangle> verticalBarriers = new List<Rectangle>();
  178. /// <summary>
  179. /// The list of horizontal barriers in the game world.
  180. /// </summary>
  181. /// <remarks>This list is not owned by this object.</remarks>
  182. private List<Rectangle> horizontalBarriers = new List<Rectangle>();
  183. /// <summary>
  184. /// The particle-effect manager for the game.
  185. /// </summary>
  186. ParticleEffectManager particleEffectManager;
  187. /// <summary>
  188. /// The network session for the game.
  189. /// </summary>
  190. private NetworkSession networkSession;
  191. /// <summary>
  192. /// The packet writer for all of the data for the world.
  193. /// </summary>
  194. private PacketWriter packetWriter = new PacketWriter();
  195. /// <summary>
  196. /// The packet reader for all of the data for the world.
  197. /// </summary>
  198. private PacketReader packetReader = new PacketReader();
  199. /// <summary>
  200. /// The number of updates that have passed since the world data was sent.
  201. /// </summary>
  202. private int updatesSinceWorldDataSend = 0;
  203. /// <summary>
  204. /// The number of updates that have passed since a status packet was sent.
  205. /// </summary>
  206. private int updatesSinceStatusPacket = 0;
  207. /// <summary>
  208. /// Construct a new World object.
  209. /// </summary>
  210. /// <param name="graphicsDevice">The graphics device used for this game.</param>
  211. /// <param name="networkSession">The network session for this game.</param>
  212. public World(GraphicsDevice graphicsDevice, ContentManager contentManager,
  213. NetworkSession networkSession)
  214. {
  215. // safety-check the parameters, as they must be valid
  216. if (graphicsDevice == null)
  217. {
  218. throw new ArgumentNullException("graphicsDevice");
  219. }
  220. if (contentManager == null)
  221. {
  222. throw new ArgumentNullException("contentManager");
  223. }
  224. if (networkSession == null)
  225. {
  226. throw new ArgumentNullException("networkSession");
  227. }
  228. // apply the parameter values
  229. this.networkSession = networkSession;
  230. // set up the staggered status packet system
  231. // -- your first update happens based on where you are in the collection
  232. for (int i = 0; i < networkSession.AllGamers.Count; i++)
  233. {
  234. if (networkSession.AllGamers[i].IsLocal)
  235. {
  236. updatesSinceStatusPacket = i;
  237. break;
  238. }
  239. }
  240. // create the spritebatch
  241. spriteBatch = new SpriteBatch(graphicsDevice);
  242. // create and initialize the particle-effect manager
  243. particleEffectManager = new ParticleEffectManager(contentManager);
  244. particleEffectManager.RegisterParticleEffect(
  245. ParticleEffectType.LaserExplosion,
  246. "Particles/laserExplosion.xml", 40);
  247. particleEffectManager.RegisterParticleEffect(
  248. ParticleEffectType.MineExplosion,
  249. "Particles/mineExplosion.xml", 8);
  250. particleEffectManager.RegisterParticleEffect(
  251. ParticleEffectType.RocketExplosion,
  252. "Particles/rocketExplosion.xml", 24);
  253. particleEffectManager.RegisterParticleEffect(
  254. ParticleEffectType.RocketTrail,
  255. "Particles/rocketTrail.xml", 16);
  256. particleEffectManager.RegisterParticleEffect(
  257. ParticleEffectType.ShipExplosion,
  258. "Particles/shipExplosion.xml", 4);
  259. particleEffectManager.RegisterParticleEffect(
  260. ParticleEffectType.ShipSpawn,
  261. "Particles/shipSpawn.xml", 4);
  262. Ship.ParticleEffectManager = particleEffectManager;
  263. RocketProjectile.ParticleEffectManager = particleEffectManager;
  264. MineProjectile.ParticleEffectManager = particleEffectManager;
  265. LaserProjectile.ParticleEffectManager = particleEffectManager;
  266. // load the font
  267. playerFont = contentManager.Load<SpriteFont>("Fonts/NetRumbleFont");
  268. // load the gameplay-object textures
  269. Ship.LoadContent(contentManager);
  270. Asteroid.LoadContent(contentManager);
  271. LaserProjectile.LoadContent(contentManager);
  272. MineProjectile.LoadContent(contentManager);
  273. RocketProjectile.LoadContent(contentManager);
  274. DoubleLaserPowerUp.LoadContent(contentManager);
  275. TripleLaserPowerUp.LoadContent(contentManager);
  276. RocketPowerUp.LoadContent(contentManager);
  277. // load the non-gameplay-object textures
  278. chatAbleTexture = contentManager.Load<Texture2D>("Textures/chatAble");
  279. chatMuteTexture = contentManager.Load<Texture2D>("Textures/chatMute");
  280. chatTalkingTexture = contentManager.Load<Texture2D>("Textures/chatTalking");
  281. readyTexture = contentManager.Load<Texture2D>("Textures/ready");
  282. cornerBarrierTexture =
  283. contentManager.Load<Texture2D>("Textures/barrierEnd");
  284. verticalBarrierTexture =
  285. contentManager.Load<Texture2D>("Textures/barrierPurple");
  286. horizontalBarrierTexture =
  287. contentManager.Load<Texture2D>("Textures/barrierRed");
  288. // clear the collision manager
  289. CollisionManager.Collection.Clear();
  290. // add the collision version of the edge barriers
  291. CollisionManager.Barriers.Clear();
  292. CollisionManager.Barriers.Add(new Rectangle(dimensions.X, dimensions.Y,
  293. dimensions.Width, barrierSize)); // top edge
  294. CollisionManager.Barriers.Add(new Rectangle(
  295. dimensions.X, dimensions.Y + dimensions.Height,
  296. dimensions.Width, barrierSize)); // bottom edge
  297. CollisionManager.Barriers.Add(new Rectangle(dimensions.X, dimensions.Y,
  298. barrierSize, dimensions.Height)); // left edge
  299. CollisionManager.Barriers.Add(new Rectangle(
  300. dimensions.X + dimensions.Width, dimensions.Y,
  301. barrierSize, dimensions.Height)); // right edge
  302. // add the rendering version of the edge barriers
  303. cornerBarriers.Clear();
  304. cornerBarriers.Add(new Rectangle(dimensions.X, dimensions.Y,
  305. barrierSize, barrierSize)); // top-left corner
  306. cornerBarriers.Add(new Rectangle(
  307. dimensions.X + dimensions.Width, dimensions.Y,
  308. barrierSize, barrierSize)); // top-right corner
  309. cornerBarriers.Add(new Rectangle(
  310. dimensions.X, dimensions.Y + dimensions.Height,
  311. barrierSize, barrierSize)); // bottom-left corner
  312. cornerBarriers.Add(new Rectangle(
  313. dimensions.X + dimensions.Width, dimensions.Y + dimensions.Height,
  314. barrierSize, barrierSize)); // bottom-right corner
  315. verticalBarriers.Clear();
  316. for (int i = 1; i < barrierCounts.Y; i++)
  317. {
  318. verticalBarriers.Add(new Rectangle(
  319. dimensions.X, dimensions.Y + barrierSize * i,
  320. barrierSize, barrierSize)); // top edge
  321. verticalBarriers.Add(new Rectangle(
  322. dimensions.X + dimensions.Width, dimensions.Y + barrierSize * i,
  323. barrierSize, barrierSize)); // bottom edge
  324. }
  325. horizontalBarriers.Clear();
  326. for (int i = 1; i < barrierCounts.X; i++)
  327. {
  328. horizontalBarriers.Add(new Rectangle(
  329. dimensions.X + barrierSize * i, dimensions.Y,
  330. barrierSize, barrierSize)); // left edge
  331. horizontalBarriers.Add(new Rectangle(
  332. dimensions.X + barrierSize * i, dimensions.Y + dimensions.Width,
  333. barrierSize, barrierSize)); // right edge
  334. }
  335. }
  336. /// <summary>
  337. /// Generate the initial state of the game, and send it to everyone.
  338. /// </summary>
  339. public void GenerateWorld()
  340. {
  341. if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
  342. {
  343. // write the identification value
  344. packetWriter.Write((int)PacketTypes.WorldSetup);
  345. // place the ships
  346. // -- we always write the maximum number of players, making the packet
  347. // predictable, in case the player count changes on the client before
  348. // this packet is received
  349. for (int i = 0; i < MaximumPlayers; i++)
  350. {
  351. Vector2 position = Vector2.Zero;
  352. if (i < networkSession.AllGamers.Count)
  353. {
  354. PlayerData playerData = networkSession.AllGamers[i].Tag
  355. as PlayerData;
  356. if ((playerData != null) && (playerData.Ship != null))
  357. {
  358. playerData.Ship.Initialize();
  359. position = playerData.Ship.Position =
  360. CollisionManager.FindSpawnPoint(playerData.Ship,
  361. playerData.Ship.Radius * 5f);
  362. playerData.Ship.Score = 0;
  363. }
  364. }
  365. // write the ship position
  366. packetWriter.Write(position);
  367. }
  368. // place the asteroids
  369. // -- for simplicity, the same number of asteroids is always the same
  370. for (int i = 0; i < asteroids.Length; i++)
  371. {
  372. // choose one of three radii
  373. float radius = 32f;
  374. switch (RandomMath.Random.Next(3))
  375. {
  376. case 0:
  377. radius = 32f;
  378. break;
  379. case 1:
  380. radius = 60f;
  381. break;
  382. case 2:
  383. radius = 96f;
  384. break;
  385. }
  386. // create the asteroid
  387. asteroids[i] = new Asteroid(radius);
  388. // write the radius
  389. packetWriter.Write(asteroids[i].Radius);
  390. // choose a variation
  391. asteroids[i].Variation = i % Asteroid.Variations;
  392. // write the variation
  393. packetWriter.Write(asteroids[i].Variation);
  394. // initialize the asteroid and it's starting position
  395. asteroids[i].Initialize();
  396. asteroids[i].Position =
  397. CollisionManager.FindSpawnPoint(asteroids[i],
  398. asteroids[i].Radius);
  399. // write the starting position and velocity
  400. packetWriter.Write(asteroids[i].Position);
  401. packetWriter.Write(asteroids[i].Velocity);
  402. }
  403. // send the packet to everyone
  404. networkSession.LocalGamers[0].SendData(packetWriter,
  405. SendDataOptions.ReliableInOrder);
  406. }
  407. }
  408. /// <summary>
  409. /// Reset per-round game state flags (shared by Initialize and host GenerateWorld).
  410. /// </summary>
  411. private void ResetGameState()
  412. {
  413. gameWon = false;
  414. winnerIndex = -1;
  415. gameExited = false;
  416. }
  417. /// <summary>
  418. /// Initialize the world with the data from the WorldSetup packet.
  419. /// </summary>
  420. /// <param name="packetReader">The packet reader with the world data.</param>
  421. public void Initialize()
  422. {
  423. // reset the game status
  424. ResetGameState();
  425. // initialize the ships with the data from the packet
  426. for (int i = 0; i < MaximumPlayers; i++)
  427. {
  428. // read each of the positions
  429. Vector2 position = packetReader.ReadVector2();
  430. // use the position value if we know of that many players
  431. if (i < networkSession.AllGamers.Count)
  432. {
  433. PlayerData playerData = networkSession.AllGamers[i].Tag
  434. as PlayerData;
  435. if ((playerData != null) && (playerData.Ship != null))
  436. {
  437. // initialize the ship with the provided position
  438. playerData.Ship.Position = position;
  439. playerData.Ship.Score = 0;
  440. playerData.Ship.Initialize();
  441. }
  442. }
  443. }
  444. // initialize the ships with the data from the packet
  445. for (int i = 0; i < asteroids.Length; i++)
  446. {
  447. float radius = packetReader.ReadSingle();
  448. if (asteroids[i] == null)
  449. {
  450. asteroids[i] = new Asteroid(radius);
  451. }
  452. asteroids[i].Variation = packetReader.ReadInt32();
  453. asteroids[i].Position = packetReader.ReadVector2();
  454. asteroids[i].Initialize();
  455. asteroids[i].Velocity = packetReader.ReadVector2();
  456. }
  457. // set the initialized state
  458. initialized = true;
  459. }
  460. /// <summary>
  461. /// Update the world.
  462. /// </summary>
  463. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  464. /// <param name="paused">If true, the game is paused.</param>
  465. public void Update(float elapsedTime, bool paused)
  466. {
  467. if (gameWon)
  468. {
  469. // update the particle-effect manager
  470. particleEffectManager.Update(elapsedTime);
  471. // make sure the collision manager is empty
  472. CollisionManager.Collection.ApplyPendingRemovals();
  473. if (CollisionManager.Collection.Count > 0)
  474. {
  475. CollisionManager.Collection.Clear();
  476. }
  477. }
  478. else
  479. {
  480. // process all incoming packets
  481. ProcessPackets();
  482. // if the game is in progress, update the state of it
  483. if (initialized && (networkSession != null) &&
  484. (networkSession.SessionState == NetworkSessionState.Playing))
  485. {
  486. // presence support
  487. int highScore = int.MinValue;
  488. int highScoreIndex = -1;
  489. for (int i = 0; i < networkSession.AllGamers.Count; i++)
  490. {
  491. NetworkGamer networkGamer = networkSession.AllGamers[i];
  492. PlayerData playerData = networkGamer.Tag as PlayerData;
  493. if ((playerData != null) && (playerData.Ship != null))
  494. {
  495. int playerScore = playerData.Ship.Score;
  496. if (playerScore == highScore)
  497. {
  498. highScorers.Add(i);
  499. }
  500. else if (playerScore > highScore)
  501. {
  502. highScorers.Clear();
  503. highScorers.Add(i);
  504. highScore = playerScore;
  505. highScoreIndex = i;
  506. }
  507. }
  508. }
  509. // the host has singular responsibilities to the game world,
  510. // that need to be done once, by one authority
  511. if (networkSession.IsHost)
  512. {
  513. // get the local player, for frequent re-use
  514. LocalNetworkGamer localGamer = networkSession.Host
  515. as LocalNetworkGamer;
  516. // check for victory
  517. // if victory has been achieved, send a packet to everyone
  518. if (highScore >= winningScore)
  519. {
  520. packetWriter.Write((int)PacketTypes.GameWon);
  521. packetWriter.Write(highScoreIndex);
  522. localGamer.SendData(packetWriter,
  523. SendDataOptions.ReliableInOrder);
  524. }
  525. // respawn each player, if it is time to do so
  526. for (int i = 0; i < networkSession.AllGamers.Count; i++)
  527. {
  528. NetworkGamer networkGamer = networkSession.AllGamers[i];
  529. PlayerData playerData = networkGamer.Tag as PlayerData;
  530. if ((playerData != null) && (playerData.Ship != null) &&
  531. !playerData.Ship.Active &&
  532. (playerData.Ship.RespawnTimer <= 0f))
  533. {
  534. // write the ship-spawn packet
  535. packetWriter.Write((int)PacketTypes.ShipSpawn);
  536. packetWriter.Write(i);
  537. packetWriter.Write(CollisionManager.FindSpawnPoint(
  538. playerData.Ship, playerData.Ship.Radius));
  539. localGamer.SendData(packetWriter,
  540. SendDataOptions.ReliableInOrder);
  541. }
  542. }
  543. // respawn the power-up if it is time to do so
  544. if (powerUp == null)
  545. {
  546. powerUpTimer -= elapsedTime;
  547. if (powerUpTimer < 0)
  548. {
  549. // write the power-up-spawn packet
  550. packetWriter.Write((int)PacketTypes.PowerUpSpawn);
  551. packetWriter.Write(RandomMath.Random.Next(3));
  552. packetWriter.Write(CollisionManager.FindSpawnPoint(null,
  553. PowerUp.PowerUpRadius * 3f));
  554. localGamer.SendData(packetWriter,
  555. SendDataOptions.ReliableInOrder);
  556. }
  557. }
  558. else
  559. {
  560. powerUpTimer = maximumPowerUpTimer;
  561. }
  562. // send everyone an update on the state of the world
  563. if (updatesSinceWorldDataSend >= updatesBetweenWorldDataSend)
  564. {
  565. packetWriter.Write((int)PacketTypes.WorldData);
  566. // write each of the asteroids
  567. for (int i = 0; i < asteroids.Length; i++)
  568. {
  569. packetWriter.Write(asteroids[i].Position);
  570. packetWriter.Write(asteroids[i].Velocity);
  571. }
  572. localGamer.SendData(packetWriter,
  573. SendDataOptions.InOrder);
  574. updatesSinceWorldDataSend = 0;
  575. }
  576. else
  577. {
  578. updatesSinceWorldDataSend++;
  579. }
  580. }
  581. // update each asteroid
  582. foreach (Asteroid asteroid in asteroids)
  583. {
  584. if (asteroid.Active)
  585. {
  586. asteroid.Update(elapsedTime);
  587. }
  588. }
  589. // update the power-up
  590. if (powerUp != null)
  591. {
  592. if (powerUp.Active)
  593. {
  594. powerUp.Update(elapsedTime);
  595. }
  596. else
  597. {
  598. powerUp = null;
  599. }
  600. }
  601. // process the local player's input
  602. if (!paused)
  603. {
  604. ProcessLocalPlayerInput();
  605. }
  606. // update each ship
  607. foreach (NetworkGamer networkGamer in networkSession.AllGamers)
  608. {
  609. PlayerData playerData = networkGamer.Tag as PlayerData;
  610. if ((playerData != null) && (playerData.Ship != null))
  611. {
  612. if (playerData.Ship.Active)
  613. {
  614. playerData.Ship.Update(elapsedTime);
  615. // check for death
  616. // -- only check on local machines - the local player is
  617. // the authority on the death of their own ship
  618. if (networkGamer.IsLocal && (playerData.Ship.Life < 0))
  619. {
  620. SendLocalShipDeath();
  621. }
  622. }
  623. else if (playerData.Ship.RespawnTimer > 0f)
  624. {
  625. playerData.Ship.RespawnTimer -= elapsedTime;
  626. if (playerData.Ship.RespawnTimer < 0f)
  627. {
  628. playerData.Ship.RespawnTimer = 0f;
  629. }
  630. }
  631. }
  632. }
  633. // update the other players with the current state of the local ship
  634. if (updatesSinceStatusPacket >= updatesBetweenStatusPackets)
  635. {
  636. updatesSinceStatusPacket = 0;
  637. SendLocalShipData();
  638. }
  639. else
  640. {
  641. updatesSinceStatusPacket++;
  642. }
  643. // update the collision manager
  644. CollisionManager.Update(elapsedTime);
  645. // update the particle-effect manager
  646. particleEffectManager.Update(elapsedTime);
  647. }
  648. }
  649. }
  650. /// <summary>
  651. /// Process the local player's input.
  652. /// </summary>
  653. private void ProcessLocalPlayerInput()
  654. {
  655. if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
  656. {
  657. // create the new input structure
  658. ShipInput shipInput = new ShipInput(
  659. GamePad.GetState(
  660. (PlayerIndex)networkSession.LocalGamers[0].SignedInGamer.PlayerIndex),
  661. Keyboard.GetState());
  662. // send it out
  663. // -- the local machine will receive and apply it from the network just
  664. // like the other clients
  665. shipInput.Serialize(packetWriter);
  666. networkSession.LocalGamers[0].SendData(packetWriter,
  667. SendDataOptions.InOrder);
  668. }
  669. }
  670. /// <summary>
  671. /// Send the current state of the ship to the other players.
  672. /// </summary>
  673. private void SendLocalShipData()
  674. {
  675. if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
  676. {
  677. PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData;
  678. if ((playerData != null) && (playerData.Ship != null))
  679. {
  680. packetWriter.Write((int)World.PacketTypes.ShipData);
  681. packetWriter.Write(playerData.Ship.Position);
  682. packetWriter.Write(playerData.Ship.Velocity);
  683. packetWriter.Write(playerData.Ship.Rotation);
  684. packetWriter.Write(playerData.Ship.Life);
  685. packetWriter.Write(playerData.Ship.Shield);
  686. packetWriter.Write(playerData.Ship.Score);
  687. networkSession.LocalGamers[0].SendData(packetWriter,
  688. SendDataOptions.InOrder);
  689. }
  690. }
  691. }
  692. /// <summary>
  693. /// Send a notification of the death of the local ship to the other players.
  694. /// </summary>
  695. private void SendLocalShipDeath()
  696. {
  697. if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
  698. {
  699. LocalNetworkGamer localNetworkGamer = networkSession.LocalGamers[0]
  700. as LocalNetworkGamer;
  701. PlayerData playerData = localNetworkGamer.Tag as PlayerData;
  702. if ((playerData != null) && (playerData.Ship != null))
  703. {
  704. // send a ship-death notification
  705. packetWriter.Write((int)PacketTypes.ShipDeath);
  706. // determine the player behind the last damage taken
  707. int lastDamagedByPlayer = -1;
  708. Ship lastDamagedByShip = playerData.Ship.LastDamagedBy as Ship;
  709. if ((lastDamagedByShip != null) &&
  710. (lastDamagedByShip != playerData.Ship))
  711. {
  712. for (int i = 0; i < networkSession.AllGamers.Count; i++)
  713. {
  714. PlayerData sourcePlayerData =
  715. networkSession.AllGamers[i].Tag as PlayerData;
  716. if ((sourcePlayerData != null) &&
  717. (sourcePlayerData.Ship != null) &&
  718. (sourcePlayerData.Ship == lastDamagedByShip))
  719. {
  720. lastDamagedByPlayer = i;
  721. break;
  722. }
  723. }
  724. }
  725. packetWriter.Write(lastDamagedByPlayer);
  726. localNetworkGamer.SendData(packetWriter,
  727. SendDataOptions.ReliableInOrder);
  728. }
  729. }
  730. }
  731. /// <summary>
  732. /// Process incoming packets on the local gamer.
  733. /// </summary>
  734. private void ProcessPackets()
  735. {
  736. if ((networkSession != null) && (networkSession.LocalGamers.Count > 0))
  737. {
  738. // process all packets found, every frame
  739. while (networkSession.LocalGamers[0].IsDataAvailable)
  740. {
  741. NetworkGamer sender;
  742. networkSession.LocalGamers[0].ReceiveData(packetReader, out sender);
  743. // read the type of packet...
  744. PacketTypes packetType = (PacketTypes)packetReader.ReadInt32();
  745. // ... and dispatch appropriately
  746. switch (packetType)
  747. {
  748. case PacketTypes.PlayerData:
  749. UpdatePlayerData(sender);
  750. break;
  751. case PacketTypes.WorldSetup:
  752. // apply the world setup data, but only once
  753. if (!Initialized)
  754. {
  755. Initialize();
  756. }
  757. break;
  758. case PacketTypes.ShipData:
  759. if ((sender != null) && !sender.IsLocal)
  760. {
  761. UpdateShipData(sender);
  762. }
  763. break;
  764. case PacketTypes.WorldData:
  765. if (!networkSession.IsHost && Initialized)
  766. {
  767. UpdateWorldData();
  768. }
  769. break;
  770. case PacketTypes.ShipInput:
  771. if (sender != null)
  772. {
  773. PlayerData playerData = sender.Tag as PlayerData;
  774. if ((playerData != null) && (playerData.Ship != null))
  775. {
  776. playerData.Ship.ShipInput =
  777. new ShipInput(packetReader);
  778. }
  779. }
  780. break;
  781. case PacketTypes.ShipSpawn:
  782. SpawnShip();
  783. break;
  784. case PacketTypes.PowerUpSpawn:
  785. SpawnPowerup();
  786. break;
  787. case PacketTypes.ShipDeath:
  788. KillShip(sender);
  789. break;
  790. case PacketTypes.GameWon:
  791. gameWon = true;
  792. winnerIndex = packetReader.ReadInt32();
  793. if (networkSession.IsHost && (networkSession.SessionState ==
  794. NetworkSessionState.Playing))
  795. {
  796. networkSession.EndGame();
  797. }
  798. break;
  799. }
  800. }
  801. }
  802. }
  803. /// <summary>
  804. /// Spawn a ship based on the data in the packet.
  805. /// </summary>
  806. private void SpawnShip()
  807. {
  808. int whichGamer = packetReader.ReadInt32();
  809. if (whichGamer < networkSession.AllGamers.Count)
  810. {
  811. NetworkGamer networkGamer = networkSession.AllGamers[whichGamer];
  812. PlayerData playerData = networkGamer.Tag as PlayerData;
  813. if ((playerData != null) && (playerData.Ship != null))
  814. {
  815. playerData.Ship.Position = packetReader.ReadVector2();
  816. playerData.Ship.Initialize();
  817. }
  818. }
  819. }
  820. /// <summary>
  821. /// Spawn a power-up based on the data in the packet.
  822. /// </summary>
  823. private void SpawnPowerup()
  824. {
  825. int whichPowerUp = packetReader.ReadInt32();
  826. if (powerUp == null)
  827. {
  828. switch (whichPowerUp)
  829. {
  830. case 0:
  831. powerUp = new DoubleLaserPowerUp();
  832. break;
  833. case 1:
  834. powerUp = new TripleLaserPowerUp();
  835. break;
  836. case 2:
  837. powerUp = new RocketPowerUp();
  838. break;
  839. }
  840. }
  841. if (powerUp != null)
  842. {
  843. powerUp.Position = packetReader.ReadVector2();
  844. powerUp.Initialize();
  845. }
  846. }
  847. /// <summary>
  848. /// Kill the sender's ship based on data in the packet.
  849. /// </summary>
  850. /// <param name="sender">The sender of the packet.</param>
  851. private void KillShip(NetworkGamer sender)
  852. {
  853. if (sender != null)
  854. {
  855. PlayerData playerData = sender.Tag as PlayerData;
  856. if ((playerData != null) && (playerData.Ship != null) &&
  857. playerData.Ship.Active)
  858. {
  859. GameplayObject source = null;
  860. // read the index of the source of the last damage taken
  861. int sourcePlayerIndex = packetReader.ReadInt32();
  862. if ((sourcePlayerIndex >= 0) &&
  863. (sourcePlayerIndex < networkSession.AllGamers.Count))
  864. {
  865. PlayerData sourcePlayerData =
  866. networkSession.AllGamers[sourcePlayerIndex].Tag
  867. as PlayerData;
  868. source = sourcePlayerData != null ? sourcePlayerData.Ship :
  869. null;
  870. }
  871. // kill the ship
  872. playerData.Ship.Die(source, false);
  873. }
  874. }
  875. }
  876. /// <summary>
  877. /// Update the player data for the sender based on the data in the packet.
  878. /// </summary>
  879. /// <param name="sender">The sender of the packet.</param>
  880. private void UpdatePlayerData(NetworkGamer sender)
  881. {
  882. if ((networkSession != null) && (networkSession.LocalGamers.Count > 0) &&
  883. (sender != null))
  884. {
  885. PlayerData playerData = sender.Tag as PlayerData;
  886. if (playerData != null)
  887. {
  888. playerData.Deserialize(packetReader);
  889. // see if we're still unique
  890. // -- this can happen legitimately as we receive introductory data
  891. foreach (LocalNetworkGamer localNetworkGamer in
  892. networkSession.LocalGamers)
  893. {
  894. PlayerData localPlayerData =
  895. localNetworkGamer.Tag as PlayerData;
  896. if ((localPlayerData != null) &&
  897. !Ship.HasUniqueColorIndex(localNetworkGamer,
  898. networkSession))
  899. {
  900. localPlayerData.ShipColor = Ship.GetNextUniqueColorIndex(
  901. localPlayerData.ShipColor, networkSession);
  902. packetWriter.Write((int)World.PacketTypes.PlayerData);
  903. localPlayerData.Serialize(packetWriter);
  904. networkSession.LocalGamers[0].SendData(packetWriter,
  905. SendDataOptions.ReliableInOrder);
  906. }
  907. }
  908. }
  909. }
  910. }
  911. /// <summary>
  912. /// Update ship state based on the data in the packet.
  913. /// </summary>
  914. /// <param name="sender">The sender of the packet.</param>
  915. private void UpdateShipData(NetworkGamer sender)
  916. {
  917. if (sender != null)
  918. {
  919. PlayerData playerData = sender.Tag as PlayerData;
  920. if ((playerData != null) && (playerData.Ship != null))
  921. {
  922. playerData.Ship.Position = packetReader.ReadVector2();
  923. playerData.Ship.Velocity = packetReader.ReadVector2();
  924. playerData.Ship.Rotation = packetReader.ReadSingle();
  925. playerData.Ship.Life = packetReader.ReadSingle();
  926. playerData.Ship.Shield = packetReader.ReadSingle();
  927. playerData.Ship.Score = packetReader.ReadInt32();
  928. }
  929. }
  930. }
  931. /// <summary>
  932. /// Update the world data based on the data in the packet.
  933. /// </summary>
  934. private void UpdateWorldData()
  935. {
  936. // safety-check the parameters, as they must be valid
  937. if (packetReader == null)
  938. {
  939. throw new ArgumentNullException("packetReader");
  940. }
  941. for (int i = 0; i < asteroids.Length; i++)
  942. {
  943. asteroids[i].Position = packetReader.ReadVector2();
  944. asteroids[i].Velocity = packetReader.ReadVector2();
  945. }
  946. }
  947. /// <summary>
  948. /// Draws the objects in the world.
  949. /// </summary>
  950. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  951. /// <param name="center">The center of the current view.</param>
  952. public void Draw(float elapsedTime, Vector2 center)
  953. {
  954. Matrix transform = Matrix.CreateTranslation(
  955. new Vector3(-center.X, -center.Y, 0f));
  956. spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied,
  957. null, null, null, null, transform);
  958. // draw the barriers
  959. foreach (Rectangle rectangle in cornerBarriers)
  960. {
  961. spriteBatch.Draw(cornerBarrierTexture, rectangle, Color.White);
  962. }
  963. foreach (Rectangle rectangle in verticalBarriers)
  964. {
  965. spriteBatch.Draw(verticalBarrierTexture, rectangle, Color.White);
  966. }
  967. foreach (Rectangle rectangle in horizontalBarriers)
  968. {
  969. spriteBatch.Draw(horizontalBarrierTexture, rectangle, Color.White);
  970. }
  971. // draw the asteroids
  972. foreach (Asteroid asteroid in asteroids)
  973. {
  974. if (asteroid.Active)
  975. {
  976. asteroid.Draw(elapsedTime, spriteBatch);
  977. }
  978. }
  979. // draw the powerup
  980. if ((powerUp != null) && powerUp.Active)
  981. {
  982. powerUp.Draw(elapsedTime, spriteBatch);
  983. }
  984. // draw the ships
  985. foreach (NetworkGamer networkGamer in networkSession.AllGamers)
  986. {
  987. PlayerData playerData = networkGamer.Tag as PlayerData;
  988. if ((playerData != null) && (playerData.Ship != null) &&
  989. playerData.Ship.Active)
  990. {
  991. playerData.Ship.Draw(elapsedTime, spriteBatch);
  992. }
  993. }
  994. // draw the alpha-blended particles
  995. particleEffectManager.Draw(spriteBatch, SpriteBlendMode.AlphaBlend);
  996. spriteBatch.End();
  997. // draw the additive particles
  998. spriteBatch.Begin(SpriteSortMode.Texture, BlendState.Additive,
  999. null, null, null, null, transform);
  1000. particleEffectManager.Draw(spriteBatch, SpriteBlendMode.Additive);
  1001. spriteBatch.End();
  1002. }
  1003. /// <summary>
  1004. /// Draw the specified player's data in the screen - gamertag, etc.
  1005. /// </summary>
  1006. /// <param name="totalTime">The total time spent in the game.</param>
  1007. /// <param name="networkGamer">The player to be drawn.</param>
  1008. /// <param name="position">The center of the desired location.</param>
  1009. /// <param name="spriteBatch">The SpriteBatch object used to draw.</param>
  1010. /// <param name="lobby">If true, drawn "lobby style"</param>
  1011. public void DrawPlayerData(float totalTime, NetworkGamer networkGamer,
  1012. Vector2 position, SpriteBatch spriteBatch, bool lobby)
  1013. {
  1014. // safety-check the parameters, as they must be valid
  1015. if (networkGamer == null)
  1016. {
  1017. throw new ArgumentNullException("networkGamer");
  1018. }
  1019. if (spriteBatch == null)
  1020. {
  1021. throw new ArgumentNullException("spriteBatch");
  1022. }
  1023. // get the player data
  1024. PlayerData playerData = networkGamer.Tag as PlayerData;
  1025. if (playerData == null)
  1026. {
  1027. return;
  1028. }
  1029. // draw the gamertag
  1030. float playerStringScale = 1.0f;
  1031. if (networkGamer.IsLocal)
  1032. {
  1033. // pulse the scale of local gamers
  1034. playerStringScale = 1f + 0.08f * (1f + (float)Math.Sin(totalTime * 4f));
  1035. }
  1036. string playerString = networkGamer.Gamertag;
  1037. Color playerColor = playerData.Ship == null ?
  1038. Ship.ShipColors[playerData.ShipColor] : playerData.Ship.Color;
  1039. Vector2 playerStringSize = playerFont.MeasureString(playerString);
  1040. Vector2 playerStringPosition = position;
  1041. spriteBatch.DrawString(playerFont, playerString, playerStringPosition,
  1042. playerColor, 0f,
  1043. new Vector2(playerStringSize.X / 2f, playerStringSize.Y / 2f),
  1044. playerStringScale, SpriteEffects.None, 0f);
  1045. // draw the chat texture
  1046. Texture2D chatTexture = null;
  1047. if (networkGamer.IsMutedByLocalUser)
  1048. {
  1049. chatTexture = chatMuteTexture;
  1050. }
  1051. else if (networkGamer.IsTalking)
  1052. {
  1053. chatTexture = chatTalkingTexture;
  1054. }
  1055. else if (networkGamer.HasVoice)
  1056. {
  1057. chatTexture = chatAbleTexture;
  1058. }
  1059. if (chatTexture != null)
  1060. {
  1061. float chatTextureScale = 0.9f * playerStringSize.Y /
  1062. (float)chatTexture.Height;
  1063. Vector2 chatTexturePosition = new Vector2(playerStringPosition.X -
  1064. 1.2f * playerStringSize.X / 2f -
  1065. 1.1f * chatTextureScale * (float)chatTexture.Width / 2f,
  1066. playerStringPosition.Y);
  1067. spriteBatch.Draw(chatTexture, chatTexturePosition, null,
  1068. Color.White, 0f, new Vector2((float)chatTexture.Width / 2f,
  1069. (float)chatTexture.Height / 2f), chatTextureScale,
  1070. SpriteEffects.None, 0f);
  1071. }
  1072. // if we're in "lobby mode", draw a sample version of the ship,
  1073. // and the ready texture
  1074. if (lobby)
  1075. {
  1076. // draw the ship
  1077. if (playerData.Ship != null)
  1078. {
  1079. float oldShipShield = playerData.Ship.Shield;
  1080. float oldShipRadius = playerData.Ship.Radius;
  1081. Vector2 oldShipPosition = playerData.Ship.Position;
  1082. float oldShipRotation = playerData.Ship.Rotation;
  1083. playerData.Ship.Shield = 0f;
  1084. playerData.Ship.Radius = 0.6f * (float)playerStringSize.Y;
  1085. playerData.Ship.Position = new Vector2(playerStringPosition.X +
  1086. 1.2f * playerStringSize.X / 2f + 1.1f * playerData.Ship.Radius,
  1087. playerStringPosition.Y);
  1088. playerData.Ship.Rotation = 0f;
  1089. playerData.Ship.Draw(0f, spriteBatch);
  1090. playerData.Ship.Rotation = oldShipRotation;
  1091. playerData.Ship.Position = oldShipPosition;
  1092. playerData.Ship.Shield = oldShipShield;
  1093. playerData.Ship.Radius = oldShipRadius;
  1094. }
  1095. // draw the ready texture
  1096. if ((readyTexture != null) && networkGamer.IsReady)
  1097. {
  1098. float readyTextureScale = 0.9f * playerStringSize.Y /
  1099. (float)readyTexture.Height;
  1100. Vector2 readyTexturePosition = new Vector2(playerStringPosition.X +
  1101. 1.2f * playerStringSize.X / 2f +
  1102. 2.2f * playerData.Ship.Radius +
  1103. 1.1f * readyTextureScale * (float)readyTexture.Width / 2f,
  1104. playerStringPosition.Y);
  1105. spriteBatch.Draw(readyTexture, readyTexturePosition, null,
  1106. Color.White, 0f, new Vector2((float)readyTexture.Width / 2f,
  1107. (float)readyTexture.Height / 2f), readyTextureScale,
  1108. SpriteEffects.None, 0f);
  1109. }
  1110. }
  1111. else
  1112. {
  1113. // if we're not in "lobby mode", draw the score
  1114. if (playerData.Ship != null)
  1115. {
  1116. string scoreString = String.Empty;
  1117. if (playerData.Ship.Active)
  1118. {
  1119. scoreString = playerData.Ship.Score.ToString();
  1120. }
  1121. else
  1122. {
  1123. int respawnTimer =
  1124. (int)Math.Ceiling(playerData.Ship.RespawnTimer);
  1125. scoreString = "Respawning in: " + respawnTimer.ToString();
  1126. }
  1127. Vector2 scoreStringSize = playerFont.MeasureString(scoreString);
  1128. Vector2 scoreStringPosition = new Vector2(position.X,
  1129. position.Y + 0.9f * playerStringSize.Y);
  1130. spriteBatch.DrawString(playerFont, scoreString, scoreStringPosition,
  1131. playerColor, 0f, new Vector2(scoreStringSize.X / 2f,
  1132. scoreStringSize.Y / 2f), 1f, SpriteEffects.None, 0f);
  1133. }
  1134. }
  1135. }
  1136. /// <summary>
  1137. /// Finalizes the World object, calls Dispose(false)
  1138. /// </summary>
  1139. ~World()
  1140. {
  1141. Dispose(false);
  1142. }
  1143. /// <summary>
  1144. /// Disposes the World object.
  1145. /// </summary>
  1146. public void Dispose()
  1147. {
  1148. Dispose(true);
  1149. GC.SuppressFinalize(this);
  1150. }
  1151. /// <summary>
  1152. /// Disposes this object.
  1153. /// </summary>
  1154. /// <param name="disposing">
  1155. /// True if this method was called as part of the Dispose method.
  1156. /// </param>
  1157. protected virtual void Dispose(bool disposing)
  1158. {
  1159. if (disposing)
  1160. {
  1161. lock (this)
  1162. {
  1163. if (packetReader != null)
  1164. {
  1165. packetReader.Close();
  1166. packetReader = null;
  1167. }
  1168. if (packetWriter != null)
  1169. {
  1170. packetWriter.Close();
  1171. packetWriter = null;
  1172. }
  1173. if (spriteBatch != null)
  1174. {
  1175. spriteBatch.Dispose();
  1176. spriteBatch = null;
  1177. }
  1178. cornerBarrierTexture = null;
  1179. verticalBarrierTexture = null;
  1180. horizontalBarrierTexture = null;
  1181. Ship.UnloadContent();
  1182. Asteroid.UnloadContent();
  1183. LaserProjectile.UnloadContent();
  1184. MineProjectile.UnloadContent();
  1185. RocketProjectile.UnloadContent();
  1186. DoubleLaserPowerUp.UnloadContent();
  1187. TripleLaserPowerUp.UnloadContent();
  1188. Ship.ParticleEffectManager = null;
  1189. RocketProjectile.ParticleEffectManager = null;
  1190. MineProjectile.ParticleEffectManager = null;
  1191. LaserProjectile.ParticleEffectManager = null;
  1192. particleEffectManager.UnregisterParticleEffect(
  1193. ParticleEffectType.MineExplosion);
  1194. particleEffectManager.UnregisterParticleEffect(
  1195. ParticleEffectType.RocketExplosion);
  1196. particleEffectManager.UnregisterParticleEffect(
  1197. ParticleEffectType.RocketTrail);
  1198. particleEffectManager.UnregisterParticleEffect(
  1199. ParticleEffectType.ShipExplosion);
  1200. particleEffectManager.UnregisterParticleEffect(
  1201. ParticleEffectType.ShipSpawn);
  1202. }
  1203. }
  1204. }
  1205. }
  1206. }