2 using System.Collections.Generic;
5 using Microsoft.Xna.Framework;
6 using Microsoft.Xna.Framework.Content;
7 using Microsoft.Xna.Framework.Graphics;
8 using Microsoft.Xna.Framework.Input;
13 /// Container class for the whole state of the game.
15 public class GameState
17 #region Public Properties
19 public int HitMonsterScore { get { return hitMonsterScore; } }
20 public int KillMonsterScore { get { return killMonsterScore; } }
22 public long FrameNumber { get { return mFrameNumber; } }
24 public long Checksum { get { return mChecksum; } }
26 public int NumberOfPlayers { get { return mNumberOfPlayers; } }
29 public List<IEntity> Entities = new List<IEntity>();
30 public List<Projectile> mProjectiles = new List<Projectile>();
31 public Player[] mCharacters;
32 public Display mDisplay;
36 #region Public Methods
39 /// Construct a game state container with the number of players.
41 /// <param name="numPlayers">Number of players.</param>
42 public GameState(int numPlayers)
44 mNumberOfPlayers = numPlayers;
47 mCharacters = new Player[numPlayers];
49 mIsGameOver = new bool[numPlayers];
50 mIsTerminated = new bool[numPlayers];
52 mMouseLocation = new Point[numPlayers];
53 mMouseButton = new bool[numPlayers];
54 mKeysDown = new List<Keys>[numPlayers];
55 for (int i = 0; i < numPlayers; i++) mKeysDown[i] = new List<Keys>();
57 mKeypressCount = new int[numPlayers];
64 /// Should be called by the Game class to advance the state
65 /// to the next frame.
67 /// <param name="inputs">The inputs that occurred to be
68 /// applied this coming frame.</param>
69 /// <param name="milliseconds">Milliseconds; used for the checksum.</param>
70 public void AdvanceFrame(NextInputs inputs, long milliseconds)
73 mElapsedTime += milliseconds;
75 for (int player = 0; player < NumberOfPlayers; player++)
77 if (inputs.IsMousePressedChanged[player])
79 mMouseButton[player] = inputs.MousePressed[player];
82 if (inputs.IsMouseLocationChanged[player])
84 mMouseLocation[player] = inputs.MouseLocation[player];
87 foreach (Keys k in inputs.KeysPressed[player])
89 if (!mKeysDown[player].Contains(k))
91 mKeysDown[player].Add(k);
92 mKeypressCount[player]++;
96 foreach (Keys k in inputs.KeysReleased[player]) mKeysDown[player].Remove(k);
104 /// Get the mouse location for a player.
106 /// <param name="playerNum">Player Number.</param>
107 /// <returns>Mouse location.</returns>
108 public Point GetMouseLocation(int playerNum)
110 return mMouseLocation[playerNum];
114 /// Get the mouse button state for a player.
116 /// <param name="playerNum">Player number.</param>
117 /// <returns>Mouse button state..</returns>
118 public bool GetMouseButton(int playerNum)
120 return mMouseButton[playerNum];
124 /// Get the keyboard state for a player.
126 /// <param name="playerNum">Player number.</param>
127 /// <returns>Keyboard state.</returns>
128 public List<Keys> GetKeysDown(int playerNum)
130 return mKeysDown[playerNum];
136 #region Private Methods
138 // Calculates a checksum for debugging network synchronization issues.
139 long ComputeChecksum()
141 mChecksum += FrameNumber;
142 for (int i = 0; i < NumberOfPlayers; i++)
144 mChecksum = mChecksum + mKeypressCount[i];
145 mChecksum = mChecksum * 3 + (mIsGameOver[i] ? 1 : 2);
146 mChecksum = mChecksum * 3 + (mIsTerminated[i] ? 1 : 2);
147 foreach (Keys k in mKeysDown[i])
148 mChecksum = mChecksum * 257 + (int)k;
149 mChecksum = mChecksum * 25789 + mMouseLocation[i].X * 259 + mMouseLocation[i].Y + 375;
150 mChecksum = mChecksum * 3 + (mMouseButton[i] ? 1 : 2);
153 mChecksum += mElapsedTime;
161 #region Private Variables
162 private const int hitMonsterScore = 20;
163 private const int killMonsterScore = 100;
164 int mNumberOfPlayers;
165 public Point[] mMouseLocation;
166 public bool[] mMouseButton;
167 public List<Keys>[] mKeysDown;
172 bool[] mIsTerminated;
174 int[] mKeypressCount;
182 /// Container class for all the inputs for a single frame.
184 public class NextInputs
186 public List<Keys>[] KeysPressed;
187 public List<Keys>[] KeysReleased;
188 public Point[] MouseLocation;
189 public bool[] IsMouseLocationChanged;
190 public bool[] MousePressed;
191 public bool[] IsMousePressedChanged;
193 public NextInputs(int numPlayers)
195 KeysPressed = new List<Keys>[numPlayers];
196 KeysReleased = new List<Keys>[numPlayers];
197 IsMouseLocationChanged = new bool[numPlayers];
198 MousePressed = new bool[numPlayers];
199 IsMousePressedChanged = new bool[numPlayers];
200 for (int i = 0; i < numPlayers; i++) KeysPressed[i] = new List<Keys>();
201 for (int i = 0; i < numPlayers; i++) KeysReleased[i] = new List<Keys>();
209 public class Game : IDeterministicGame
211 #region Public Properties
214 /// Get the content manager associated with this game.
216 public ContentManager ContentManager { get { return mContentManager; } }
221 public GameState State;
227 bool[,] grid = (bool[,])State.Map.Grid.Clone();
228 foreach (IEntity entity in State.Entities)
230 Point coordinates = entity.Coordinates;
231 grid[coordinates.X, coordinates.Y] = false;
233 foreach (Player player in State.mCharacters)
235 if (player == null) continue;
236 Point coordinates = player.Coordinates;
237 grid[coordinates.X, coordinates.Y] = false;
246 #region Public Methods
249 /// Get an entity at a certain place on the map.
251 /// <param name="point">The coordinates.</param>
252 /// <returns>The entity, or null if none is at that location.</returns>
253 public IEntity GetEntityAtCoordinates(Point point)
255 foreach (IEntity entity in State.Entities)
257 if (entity.Coordinates == point) return entity;
263 /// Get a player at a certain place on the map.
265 /// <param name="point">The coordinates.</param>
266 /// <returns>The player, or null if none is at that location.</returns>
267 public Player GetPlayerAtCoordinates(Point point)
269 foreach (Player player in State.mCharacters)
271 if (player != null && player.Coordinates == point) return player;
277 /// Determine if a cell is open, depending on the static scenery
278 /// of the map and if there are any collidable entities.
280 /// <param name="point">The coordinates.</param>
281 /// <returns>True if the cell is open; false otherwise.</returns>
282 public bool IsCellOpen(Point point)
284 if (!State.Map.IsCellOpen(point)) return false;
285 IEntity entity = GetEntityAtCoordinates(point);
286 if (entity != null && entity.IsCollidable) return false;
287 Player player = GetPlayerAtCoordinates(point);
288 if (player != null) return false;
293 /// Remove a specific entity from the game. The entity can still
294 /// be tracked some other way, but it won't included when the game is
295 /// updating and drawing stuff.
297 /// <param name="entity">The entity.</param>
298 /// <returns>The entity that was removed, or null if no entity was removed.</returns>
299 public IEntity RemoveEntity(IEntity entity)
301 if (State.Entities.Remove(entity)) return entity;
306 /// Move on to the next map, and advance the level.
308 public void AdvanceLevel()
310 // TODO: Load the next map, etc...
311 //TODO somehow get next map
312 State.Entities.Clear();
313 String nextMap = State.Map.Next;
314 State.Map = mContentManager.Load<Map>("Maps/"+nextMap);
315 for (int i = 0; i < State.mCharacters.Length; i++)
317 State.mCharacters[i].Coordinates = State.Map.GetStartingPositionForPlayer(i + 1);
319 State.Entities = State.Map.GetAllEntities(this);
323 /// Restart the current level.
328 // TODO: Do other stuff to reset everything.
336 public void LoadContent(ContentManager contentManager)
338 mContentManager = contentManager;
339 menu = mContentManager.Load<SpriteFont>("menuFont");
343 public void UnloadContent()
347 private int GetPlayerNumber(Object playerIdentifier)
349 for (int i = 0; i < mPlayerIdentifiers.Length; i++)
351 if (mPlayerIdentifiers[i] == playerIdentifier) return i;
353 throw new Exception("Illegal player identifier" + playerIdentifier);
356 public Vector2 PreferredScreenSize
358 get { return new Vector2(800, 600); }
361 public int MinimumSupportedPlayers
366 public int MaximumSupportedPlayers
371 public void ResetGame(object[] playerIdentifiers, object thisPlayer)
373 int numPlayers = playerIdentifiers.Count();
375 mPlayerIdentifiers = new object[numPlayers];
376 for (int i = 0; i < numPlayers; i++) mPlayerIdentifiers[i] = playerIdentifiers[i];
378 mThisPlayerID = GetPlayerNumber(thisPlayer);
380 State = new GameState(numPlayers);
381 mInputs = new NextInputs(numPlayers);
382 State.mDisplay = new Display(this);
383 State.mDisplay.LoadContent(mContentManager);
386 Texture2D mapTiles = mContentManager.Load<Texture2D>("graphics/wallAndFloorTilesNoEdgeScale");
387 Tilemap tilemap = new Tilemap(mapTiles, 10, 7);
388 tilemap.SetTile('`', new Point(0, 2), TileFlags.Closed | TileFlags.Wall);
389 tilemap.SetTile('~', new Point(1, 2), TileFlags.Closed | TileFlags.Wall);
390 tilemap.SetTile('!', new Point(2, 2), TileFlags.Closed | TileFlags.Wall);
391 tilemap.SetTile('@', new Point(3, 2), TileFlags.Closed | TileFlags.Wall);
392 tilemap.SetTile('#', new Point(4, 2), TileFlags.Closed | TileFlags.Wall);
393 tilemap.SetTile('$', new Point(5, 2), TileFlags.Closed | TileFlags.Wall);
394 tilemap.SetTile('%', new Point(6, 2), TileFlags.Closed | TileFlags.Wall);
395 tilemap.SetTile('^', new Point(8, 2), TileFlags.Closed | TileFlags.Wall);
396 tilemap.SetTile('&', new Point(9, 2), TileFlags.Closed | TileFlags.Wall);
397 tilemap.SetTile('*', new Point(0, 3), TileFlags.Closed | TileFlags.Wall);
398 tilemap.SetTile('(', new Point(1, 3), TileFlags.Closed | TileFlags.Wall);
399 tilemap.SetTile(')', new Point(2, 3), TileFlags.Closed | TileFlags.Wall);
400 tilemap.SetTile('-', new Point(3, 3), TileFlags.Closed | TileFlags.Wall);
401 tilemap.SetTile('=', new Point(4, 3), TileFlags.Closed | TileFlags.Wall);
402 tilemap.SetTile('_', new Point(5, 3), TileFlags.Closed | TileFlags.Wall);
403 tilemap.SetTile('+', new Point(6, 3), TileFlags.Closed | TileFlags.Wall);
404 tilemap.SetTile('|', new Point(8, 3), TileFlags.Closed | TileFlags.Wall);
405 tilemap.SetTile('[', new Point(0, 4), TileFlags.Default);
406 tilemap.SetTile(']', new Point(1, 4), TileFlags.Default);
407 tilemap.SetTile('{', new Point(2, 4), TileFlags.Default);
408 tilemap.SetTile('}', new Point(3, 4), TileFlags.Default);
409 tilemap.SetTile('?', new Point(4, 4), TileFlags.Default);
410 tilemap.SetTile(',', new Point(7, 4), TileFlags.Default);
411 tilemap.SetTile('.', new Point(8, 4), TileFlags.Default);
412 tilemap.SetTile('\\', new Point(9, 4), TileFlags.Default);
413 tilemap.SetTile(';', new Point(0, 5), TileFlags.Default);
414 tilemap.SetTile(':', new Point(1, 5), TileFlags.Default);
415 tilemap.SetTile('\'', new Point(2, 5), TileFlags.Default);
416 tilemap.SetTile('"', new Point(3, 5), TileFlags.Default);
417 tilemap.SetTile(' ', new Point(4, 5), TileFlags.Default);
418 tilemap.SetTile('<', new Point(7, 5), TileFlags.Default);
419 tilemap.SetTile('>', new Point(8, 5), TileFlags.Default);
420 tilemap.SetTile('/', new Point(9, 5), TileFlags.Default);
421 Map.Tilemap = tilemap;
423 // Load the first map.
424 State.Map = mContentManager.Load<Map>("Maps/colosseumClosed");
425 State.Entities = State.Map.GetAllEntities(this);
427 mAIData = new AI(this);
431 for (int i = 0; i < PlayerIdentifiers.Length; i++)
433 Human player = new Human(mMap, "");
434 mPlayers.Add(player);
435 mDisplay.AddCharacters(player);
436 mPlayers.Add(player);
437 mDisplay.AddCharacters(player);
439 this.playerIdentifiers = PlayerIdentifiers;
440 for (int i = 0; i < mPlayers.Count; i++)
442 Point starting = mMap.GetStartingPositionForPlayer(i + 1);
443 mPlayers[i].Spawn(new Vector2(starting.X, starting.Y));
448 public long CurrentFrameNumber
450 get { return State.FrameNumber; }
453 public long CurrentChecksum
458 public void ApplyKeyInput(object playerIdentifier, Keys key, bool isKeyPressed)
460 //code from Prof Jensen's TestHarness
461 int player = GetPlayerNumber(playerIdentifier);
463 if (isKeyPressed && !mInputs.KeysPressed[player].Contains(key))
464 mInputs.KeysPressed[player].Add(key);
466 if (!isKeyPressed && !mInputs.KeysReleased[player].Contains(key))
467 mInputs.KeysReleased[player].Add(key);
471 public void ApplyMouseLocationInput(object playerIdentifier, int x, int y)
476 public void ApplyMouseButtonInput(object playerIdentifier, bool isButtonPressed)
481 public bool IsGameOver(object playerIdentifier)
486 public bool IsTerminated(object playerIdentifier)
491 public long Update(TimeSpan elapsedTime)
493 State.AdvanceFrame(mInputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state.
494 State.mDisplay.Update(elapsedTime, mThisPlayerID);
495 State.Entities.ForEach(delegate(IEntity e)
497 IMonster m = e as IMonster;
500 if (State.mCharacters[0] != null && mAIData.spaceVisible(e.Coordinates, State.mCharacters[0].Coordinates))
502 m.Chasing(State.mCharacters[0].Coordinates);
509 e.Update(elapsedTime);
511 //State.Entities.ForEach(delegate(IEntity e) { e.Update(elapsedTime); });
512 mInputs = new NextInputs(State.NumberOfPlayers); // Start with inputs cleared on the next frame.
513 //mDisplay.Update(elapsedTime);
514 return State.FrameNumber;
518 public long Draw(SpriteBatch spriteBatch)
520 bool allCharactersSelected = true;
521 for (int i = 0; i < State.NumberOfPlayers; i++)
523 //If player has not selected a player yet let them select one.
524 if (State.mCharacters[i] == null)
526 allCharactersSelected = false;
527 if (State.GetKeysDown(i).Contains(Keys.M))
529 State.mCharacters[i] = new Melee(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i);
530 State.mCharacters[i].LoadContent(mContentManager);
532 else if (State.GetKeysDown(i).Contains(Keys.R))
534 State.mCharacters[i] = new Ranged(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i);
535 State.mCharacters[i].LoadContent(mContentManager);
539 if (allCharactersSelected)
542 State.mDisplay.Draw(spriteBatch);
546 spriteBatch.GraphicsDevice.Clear(Color.Black);
547 spriteBatch.DrawString(menu, "Press R to select a Ranged Character and M to select a Melee Character", new Vector2(30, 30), Color.RosyBrown);
549 return CurrentFrameNumber;
555 #region Private Variables
559 ContentManager mContentManager;
562 Object[] mPlayerIdentifiers;