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
18 private const int hitMonsterScore = 20;
19 private const int killMonsterScore = 100;
20 public int HitMonsterScore { get { return hitMonsterScore; } }
21 public int KillMonsterScore { get { return killMonsterScore; } }
23 public long FrameNumber { get { return mFrameNumber; } }
25 public long Checksum { get { return mChecksum; } }
27 public int NumberOfPlayers { get { return mNumberOfPlayers; } }
30 public List<IEntity> Entities = new List<IEntity>();
31 public List<Projectile> mProjectiles = new List<Projectile>();
32 public Player[] mCharacters;
33 public Display mDisplay;
38 #region Public Methods
41 /// Construct a game state container with the number of players.
43 /// <param name="numPlayers">Number of players.</param>
44 public GameState(int numPlayers)
46 mNumberOfPlayers = numPlayers;
49 mCharacters = new Player[numPlayers];
51 mIsGameOver = new bool[numPlayers];
52 mIsTerminated = new bool[numPlayers];
54 mMouseLocation = new Point[numPlayers];
55 mMouseButton = new bool[numPlayers];
56 mKeysDown = new List<Keys>[numPlayers];
57 for (int i = 0; i < numPlayers; i++) mKeysDown[i] = new List<Keys>();
59 mKeypressCount = new int[numPlayers];
66 /// Should be called by the Game class to advance the state
67 /// to the next frame.
69 /// <param name="inputs">The inputs that occurred to be
70 /// applied this coming frame.</param>
71 /// <param name="milliseconds">Milliseconds; used for the checksum.</param>
72 public void AdvanceFrame(NextInputs inputs, long milliseconds)
75 mElapsedTime += milliseconds;
77 for (int player = 0; player < NumberOfPlayers; player++)
79 if (inputs.IsMousePressedChanged[player])
81 mMouseButton[player] = inputs.MousePressed[player];
84 if (inputs.IsMouseLocationChanged[player])
86 mMouseLocation[player] = inputs.MouseLocation[player];
89 foreach (Keys k in inputs.KeysPressed[player])
91 if (!mKeysDown[player].Contains(k))
93 mKeysDown[player].Add(k);
94 mKeypressCount[player]++;
98 foreach (Keys k in inputs.KeysReleased[player]) mKeysDown[player].Remove(k);
106 /// Get the mouse location for a player.
108 /// <param name="playerNum">Player Number.</param>
109 /// <returns>Mouse location.</returns>
110 public Point GetMouseLocation(int playerNum)
112 return mMouseLocation[playerNum];
116 /// Get the mouse button state for a player.
118 /// <param name="playerNum">Player number.</param>
119 /// <returns>Mouse button state..</returns>
120 public bool GetMouseButton(int playerNum)
122 return mMouseButton[playerNum];
126 /// Get the keyboard state for a player.
128 /// <param name="playerNum">Player number.</param>
129 /// <returns>Keyboard state.</returns>
130 public List<Keys> GetKeysDown(int playerNum)
132 return mKeysDown[playerNum];
138 #region Private Methods
140 // Calculates a checksum for debugging network synchronization issues.
141 long ComputeChecksum()
143 mChecksum += FrameNumber;
144 for (int i = 0; i < NumberOfPlayers; i++)
146 mChecksum = mChecksum + mKeypressCount[i];
147 mChecksum = mChecksum * 3 + (mIsGameOver[i] ? 1 : 2);
148 mChecksum = mChecksum * 3 + (mIsTerminated[i] ? 1 : 2);
149 foreach (Keys k in mKeysDown[i])
150 mChecksum = mChecksum * 257 + (int)k;
151 mChecksum = mChecksum * 25789 + mMouseLocation[i].X * 259 + mMouseLocation[i].Y + 375;
152 mChecksum = mChecksum * 3 + (mMouseButton[i] ? 1 : 2);
155 mChecksum += mElapsedTime;
163 #region Private Variables
165 int mNumberOfPlayers;
166 public Point[] mMouseLocation;
167 public bool[] mMouseButton;
168 public List<Keys>[] mKeysDown;
173 bool[] mIsTerminated;
175 int[] mKeypressCount;
183 /// Container class for all the inputs for a single frame.
185 public class NextInputs
187 public List<Keys>[] KeysPressed;
188 public List<Keys>[] KeysReleased;
189 public Point[] MouseLocation;
190 public bool[] IsMouseLocationChanged;
191 public bool[] MousePressed;
192 public bool[] IsMousePressedChanged;
194 public NextInputs(int numPlayers)
196 KeysPressed = new List<Keys>[numPlayers];
197 KeysReleased = new List<Keys>[numPlayers];
198 IsMouseLocationChanged = new bool[numPlayers];
199 MousePressed = new bool[numPlayers];
200 IsMousePressedChanged = new bool[numPlayers];
201 for (int i = 0; i < numPlayers; i++) KeysPressed[i] = new List<Keys>();
202 for (int i = 0; i < numPlayers; i++) KeysReleased[i] = new List<Keys>();
210 public class Game : IDeterministicGame
212 #region Public Properties
215 /// Get the content manager associated with this game.
217 public ContentManager ContentManager { get { return mContentManager; } }
222 public GameState State;
228 bool[,] grid = State.Map.Grid;
229 foreach (IEntity entity in State.Entities)
231 Point coordinates = entity.Coordinates;
232 if (State.Map.IsCellOpen(coordinates)) grid[coordinates.X, coordinates.Y] = false;
241 #region Public Methods
244 /// Get an entity at a certain place on the map.
246 /// <param name="point">The coordinates.</param>
247 /// <returns>The entity, or null if none is at that location.</returns>
248 public IEntity GetEntityAtCoordinates(Point point)
250 foreach (IEntity entity in State.Entities)
252 if (entity.Coordinates == point) return entity;
258 /// Get a player at a certain place on the map.
260 /// <param name="point">The coordinates.</param>
261 /// <returns>The player, or null if none is at that location.</returns>
262 public Player GetPlayerAtCoordinates(Point point)
264 foreach (Player player in State.mCharacters)
266 if (player != null && player.Coordinates == point) return player;
272 /// Determine if a cell is open, depending on the static scenery
273 /// of the map and if there are any collidable entities.
275 /// <param name="point">The coordinates.</param>
276 /// <returns>True if the cell is open; false otherwise.</returns>
277 public bool IsCellOpen(Point point)
279 if (!State.Map.IsCellOpen(point)) return false;
280 IEntity entity = GetEntityAtCoordinates(point);
281 if (entity != null && entity.IsCollidable) return false;
286 /// Remove a specific entity from the game. The entity can still
287 /// be tracked some other way, but it won't included when the game is
288 /// updating and drawing stuff.
290 /// <param name="entity">The entity.</param>
291 /// <returns>The entity that was removed, or null if no entity was removed.</returns>
292 public IEntity RemoveEntity(IEntity entity)
294 if (State.Entities.Remove(entity)) return entity;
299 /// Move on to the next map, and advance the level.
301 public void AdvanceLevel()
303 // TODO: Load the next map, etc...
307 /// Restart the current level.
312 // TODO: Do other stuff to reset everything.
321 /// This method should be called whenever the players want to move to a new map.
322 /// Not implemented yet. Need some way to get next map.
324 public void startNewMap()
326 //TODO somehow get next map
327 State.Entities.Clear();
328 //State.Map = State.Map.getNextMap();
329 for (int i = 0; i < State.mCharacters.Length; i++)
331 State.mCharacters[i].Coordinates = State.Map.GetStartingPositionForPlayer(i + 1);
333 State.Entities = State.Map.GetAllEntities(this);
335 public void LoadContent(ContentManager contentManager)
337 mContentManager = contentManager;
338 menu = mContentManager.Load<SpriteFont>("menuFont");
342 public void UnloadContent()
346 private int GetPlayerNumber(Object playerIdentifier)
348 for (int i = 0; i < mPlayerIdentifiers.Length; i++)
350 if (mPlayerIdentifiers[i] == playerIdentifier) return i;
352 throw new Exception("Illegal player identifier" + playerIdentifier);
355 public Vector2 PreferredScreenSize
357 get { return new Vector2(800, 600); }
360 public int MinimumSupportedPlayers
365 public int MaximumSupportedPlayers
370 public void ResetGame(object[] playerIdentifiers, object thisPlayer)
372 int numPlayers = playerIdentifiers.Count();
374 mPlayerIdentifiers = new object[numPlayers];
375 for (int i = 0; i < numPlayers; i++) mPlayerIdentifiers[i] = playerIdentifiers[i];
377 mThisPlayerID = GetPlayerNumber(thisPlayer);
379 State = new GameState(numPlayers);
380 mInputs = new NextInputs(numPlayers);
381 State.mDisplay = new Display(this);
382 State.mDisplay.LoadContent(mContentManager);
385 Texture2D mapTiles = mContentManager.Load<Texture2D>("graphics/wallAndFloorTilesNoEdgeScale");
386 Tilemap tilemap = new Tilemap(mapTiles, 10, 7);
387 tilemap.SetTile(' ', new Point(4, 5), TileFlags.Default);
388 tilemap.SetTile('`', new Point(0, 1), TileFlags.Closed | TileFlags.Wall);
389 tilemap.SetTile('~', new Point(1, 1), TileFlags.Closed | TileFlags.Wall);
390 tilemap.SetTile('!', new Point(3, 1), TileFlags.Closed | TileFlags.Wall);
391 tilemap.SetTile('@', new Point(4, 1), TileFlags.Closed | TileFlags.Wall);
392 tilemap.SetTile('#', new Point(5, 1), TileFlags.Closed | TileFlags.Wall);
393 tilemap.SetTile('$', new Point(6, 1), TileFlags.Closed | TileFlags.Wall);
394 tilemap.SetTile('%', new Point(8, 1), TileFlags.Closed | TileFlags.Wall);
395 tilemap.SetTile('^', new Point(9, 1), TileFlags.Closed | TileFlags.Wall);
396 tilemap.SetTile('&', new Point(0, 2), TileFlags.Closed | TileFlags.Wall);
397 tilemap.SetTile('=', new Point(1, 2), TileFlags.Closed | TileFlags.Wall);
398 tilemap.SetTile('*', new Point(2, 2), TileFlags.Closed | TileFlags.Wall);
399 tilemap.SetTile('(', new Point(4, 2), TileFlags.Closed | TileFlags.Wall);
400 tilemap.SetTile(')', new Point(0, 3), TileFlags.Closed | TileFlags.Wall);
401 tilemap.SetTile('_', new Point(2, 3), TileFlags.Closed | TileFlags.Wall);
402 tilemap.SetTile('-', new Point(9, 3), TileFlags.Closed | TileFlags.Wall);
403 tilemap.SetTile(',', new Point(1, 4), TileFlags.Default);
404 tilemap.SetTile('+', new Point(2, 4), TileFlags.Default);
405 tilemap.SetTile('[', new Point(3, 4), TileFlags.Default);
406 tilemap.SetTile(']', new Point(4, 4), TileFlags.Default);
407 tilemap.SetTile('{', new Point(5, 4), TileFlags.Default);
408 tilemap.SetTile('}', new Point(6, 4), TileFlags.Default);
409 tilemap.SetTile('\\', new Point(8, 4), TileFlags.Default);
410 tilemap.SetTile('|', new Point(9, 4), TileFlags.Default);
411 tilemap.SetTile(';', new Point(0, 5), TileFlags.Default);
412 tilemap.SetTile(':', new Point(1, 5), TileFlags.Default);
413 tilemap.SetTile('\'', new Point(2, 5), TileFlags.Default);
414 tilemap.SetTile('"', new Point(3, 5), TileFlags.Default);
415 tilemap.SetTile('.', new Point(5, 5), TileFlags.Default);
416 tilemap.SetTile('<', new Point(6, 5), TileFlags.Default);
417 tilemap.SetTile('>', new Point(7, 5), TileFlags.Default);
418 tilemap.SetTile('/', new Point(8, 5), TileFlags.Default);
419 tilemap.SetTile('?', new Point(9, 5), TileFlags.Default);
420 Map.Tilemap = tilemap;
422 // Load the first map.
423 State.Map = mContentManager.Load<Map>("Maps/level1");
424 State.Entities = State.Map.GetAllEntities(this);
426 //State.AIData = new AI(this);
430 for (int i = 0; i < PlayerIdentifiers.Length; i++)
432 Human player = new Human(mMap, "");
433 mPlayers.Add(player);
434 mDisplay.AddCharacters(player);
435 mPlayers.Add(player);
436 mDisplay.AddCharacters(player);
438 this.playerIdentifiers = PlayerIdentifiers;
439 for (int i = 0; i < mPlayers.Count; i++)
441 Point starting = mMap.GetStartingPositionForPlayer(i + 1);
442 mPlayers[i].Spawn(new Vector2(starting.X, starting.Y));
447 public long CurrentFrameNumber
449 get { return State.FrameNumber; }
452 public long CurrentChecksum
457 public void ApplyKeyInput(object playerIdentifier, Keys key, bool isKeyPressed)
459 //code from Prof Jensen's TestHarness
460 int player = GetPlayerNumber(playerIdentifier);
462 if (isKeyPressed && !mInputs.KeysPressed[player].Contains(key))
463 mInputs.KeysPressed[player].Add(key);
465 if (!isKeyPressed && !mInputs.KeysReleased[player].Contains(key))
466 mInputs.KeysReleased[player].Add(key);
470 public void ApplyMouseLocationInput(object playerIdentifier, int x, int y)
475 public void ApplyMouseButtonInput(object playerIdentifier, bool isButtonPressed)
480 public bool IsGameOver(object playerIdentifier)
485 public bool IsTerminated(object playerIdentifier)
490 public long Update(TimeSpan elapsedTime)
492 State.AdvanceFrame(mInputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state.
493 State.mDisplay.Update(elapsedTime, mThisPlayerID);
494 State.Entities.ForEach(delegate(IEntity e) { e.Update(elapsedTime); });
495 mInputs = new NextInputs(State.NumberOfPlayers); // Start with inputs cleared on the next frame.
496 //mDisplay.Update(elapsedTime);
497 return State.FrameNumber;
501 public long Draw(SpriteBatch spriteBatch)
503 bool allCharactersSelected = true;
504 for (int i = 0; i < State.NumberOfPlayers; i++)
506 //If player has not selected a player yet let them select one.
507 if (State.mCharacters[i] == null)
509 allCharactersSelected = false;
510 if (State.GetKeysDown(i).Contains(Keys.M))
512 State.mCharacters[i] = new Melee(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i);
513 State.mCharacters[i].LoadContent(mContentManager);
515 else if (State.GetKeysDown(i).Contains(Keys.R))
517 State.mCharacters[i] = new Ranged(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i);
518 State.mCharacters[i].LoadContent(mContentManager);
522 if (allCharactersSelected)
525 State.mDisplay.Draw(spriteBatch);
529 spriteBatch.GraphicsDevice.Clear(Color.Black);
530 spriteBatch.DrawString(menu, "Press R to select a Ranged Character and M to select a Melee Character", new Vector2(30, 30), Color.RosyBrown);
532 return CurrentFrameNumber;
538 #region Private Variables
542 ContentManager mContentManager;
545 Object[] mPlayerIdentifiers;