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 = (bool[,])State.Map.Grid.Clone();
229 foreach (IEntity entity in State.Entities)
231 Point coordinates = entity.Coordinates;
232 grid[coordinates.X, coordinates.Y] = false;
234 foreach (Player player in State.mCharacters)
236 if (player == null) continue;
237 Point coordinates = player.Coordinates;
238 grid[coordinates.X, coordinates.Y] = false;
247 #region Public Methods
250 /// Get an entity at a certain place on the map.
252 /// <param name="point">The coordinates.</param>
253 /// <returns>The entity, or null if none is at that location.</returns>
254 public IEntity GetEntityAtCoordinates(Point point)
256 foreach (IEntity entity in State.Entities)
258 if (entity.Coordinates == point) return entity;
264 /// Get a player at a certain place on the map.
266 /// <param name="point">The coordinates.</param>
267 /// <returns>The player, or null if none is at that location.</returns>
268 public Player GetPlayerAtCoordinates(Point point)
270 foreach (Player player in State.mCharacters)
272 if (player != null && player.Coordinates == point) return player;
278 /// Determine if a cell is open, depending on the static scenery
279 /// of the map and if there are any collidable entities.
281 /// <param name="point">The coordinates.</param>
282 /// <returns>True if the cell is open; false otherwise.</returns>
283 public bool IsCellOpen(Point point)
285 if (!State.Map.IsCellOpen(point)) return false;
286 IEntity entity = GetEntityAtCoordinates(point);
287 if (entity != null && entity.IsCollidable) return false;
288 Player player = GetPlayerAtCoordinates(point);
289 if (player != null) return false;
294 /// Remove a specific entity from the game. The entity can still
295 /// be tracked some other way, but it won't included when the game is
296 /// updating and drawing stuff.
298 /// <param name="entity">The entity.</param>
299 /// <returns>The entity that was removed, or null if no entity was removed.</returns>
300 public IEntity RemoveEntity(IEntity entity)
302 if (State.Entities.Remove(entity)) return entity;
307 /// Move on to the next map, and advance the level.
309 public void AdvanceLevel()
311 // TODO: Load the next map, etc...
315 /// Restart the current level.
320 // TODO: Do other stuff to reset everything.
329 /// This method should be called whenever the players want to move to a new map.
330 /// Not implemented yet. Need some way to get next map.
332 public void startNewMap()
334 //TODO somehow get next map
335 State.Entities.Clear();
336 //State.Map = State.Map.getNextMap();
337 for (int i = 0; i < State.mCharacters.Length; i++)
339 State.mCharacters[i].Coordinates = State.Map.GetStartingPositionForPlayer(i + 1);
341 State.Entities = State.Map.GetAllEntities(this);
343 public void LoadContent(ContentManager contentManager)
345 mContentManager = contentManager;
346 menu = mContentManager.Load<SpriteFont>("menuFont");
350 public void UnloadContent()
354 private int GetPlayerNumber(Object playerIdentifier)
356 for (int i = 0; i < mPlayerIdentifiers.Length; i++)
358 if (mPlayerIdentifiers[i] == playerIdentifier) return i;
360 throw new Exception("Illegal player identifier" + playerIdentifier);
363 public Vector2 PreferredScreenSize
365 get { return new Vector2(800, 600); }
368 public int MinimumSupportedPlayers
373 public int MaximumSupportedPlayers
378 public void ResetGame(object[] playerIdentifiers, object thisPlayer)
380 int numPlayers = playerIdentifiers.Count();
382 mPlayerIdentifiers = new object[numPlayers];
383 for (int i = 0; i < numPlayers; i++) mPlayerIdentifiers[i] = playerIdentifiers[i];
385 mThisPlayerID = GetPlayerNumber(thisPlayer);
387 State = new GameState(numPlayers);
388 mInputs = new NextInputs(numPlayers);
389 State.mDisplay = new Display(this);
390 State.mDisplay.LoadContent(mContentManager);
393 Texture2D mapTiles = mContentManager.Load<Texture2D>("graphics/wallAndFloorTilesNoEdgeScale");
394 Tilemap tilemap = new Tilemap(mapTiles, 10, 7);
395 tilemap.SetTile('`', new Point(0, 2), TileFlags.Closed | TileFlags.Wall);
396 tilemap.SetTile('~', new Point(1, 2), TileFlags.Closed | TileFlags.Wall);
397 tilemap.SetTile('!', new Point(2, 2), TileFlags.Closed | TileFlags.Wall);
398 tilemap.SetTile('@', new Point(3, 2), TileFlags.Closed | TileFlags.Wall);
399 tilemap.SetTile('#', new Point(4, 2), TileFlags.Closed | TileFlags.Wall);
400 tilemap.SetTile('$', new Point(5, 2), TileFlags.Closed | TileFlags.Wall);
401 tilemap.SetTile('%', new Point(6, 2), TileFlags.Closed | TileFlags.Wall);
402 tilemap.SetTile('^', new Point(8, 2), TileFlags.Closed | TileFlags.Wall);
403 tilemap.SetTile('&', new Point(9, 2), TileFlags.Closed | TileFlags.Wall);
404 tilemap.SetTile('*', new Point(0, 3), TileFlags.Closed | TileFlags.Wall);
405 tilemap.SetTile('(', new Point(1, 3), TileFlags.Closed | TileFlags.Wall);
406 tilemap.SetTile(')', new Point(2, 3), TileFlags.Closed | TileFlags.Wall);
407 tilemap.SetTile('-', new Point(3, 3), TileFlags.Closed | TileFlags.Wall);
408 tilemap.SetTile('=', new Point(4, 3), TileFlags.Closed | TileFlags.Wall);
409 tilemap.SetTile('_', new Point(5, 3), TileFlags.Closed | TileFlags.Wall);
410 tilemap.SetTile('+', new Point(6, 3), TileFlags.Closed | TileFlags.Wall);
411 tilemap.SetTile('|', new Point(8, 3), TileFlags.Closed | TileFlags.Wall);
412 tilemap.SetTile('[', new Point(0, 4), TileFlags.Default);
413 tilemap.SetTile(']', new Point(1, 4), TileFlags.Default);
414 tilemap.SetTile('{', new Point(2, 4), TileFlags.Default);
415 tilemap.SetTile('}', new Point(3, 4), TileFlags.Default);
416 tilemap.SetTile('?', new Point(4, 4), TileFlags.Default);
417 tilemap.SetTile(',', new Point(7, 4), TileFlags.Default);
418 tilemap.SetTile('.', new Point(8, 4), TileFlags.Default);
419 tilemap.SetTile('\\', new Point(9, 4), TileFlags.Default);
420 tilemap.SetTile(';', new Point(0, 5), TileFlags.Default);
421 tilemap.SetTile(':', new Point(1, 5), TileFlags.Default);
422 tilemap.SetTile('\'', new Point(2, 5), TileFlags.Default);
423 tilemap.SetTile('"', new Point(3, 5), TileFlags.Default);
424 tilemap.SetTile(' ', new Point(4, 5), TileFlags.Default);
425 tilemap.SetTile('<', new Point(7, 5), TileFlags.Default);
426 tilemap.SetTile('>', new Point(8, 5), TileFlags.Default);
427 tilemap.SetTile('/', new Point(9, 5), TileFlags.Default);
428 Map.Tilemap = tilemap;
430 // Load the first map.
431 State.Map = mContentManager.Load<Map>("Maps/colosseum");
432 State.Entities = State.Map.GetAllEntities(this);
434 //State.AIData = new AI(this);
438 for (int i = 0; i < PlayerIdentifiers.Length; i++)
440 Human player = new Human(mMap, "");
441 mPlayers.Add(player);
442 mDisplay.AddCharacters(player);
443 mPlayers.Add(player);
444 mDisplay.AddCharacters(player);
446 this.playerIdentifiers = PlayerIdentifiers;
447 for (int i = 0; i < mPlayers.Count; i++)
449 Point starting = mMap.GetStartingPositionForPlayer(i + 1);
450 mPlayers[i].Spawn(new Vector2(starting.X, starting.Y));
455 public long CurrentFrameNumber
457 get { return State.FrameNumber; }
460 public long CurrentChecksum
465 public void ApplyKeyInput(object playerIdentifier, Keys key, bool isKeyPressed)
467 //code from Prof Jensen's TestHarness
468 int player = GetPlayerNumber(playerIdentifier);
470 if (isKeyPressed && !mInputs.KeysPressed[player].Contains(key))
471 mInputs.KeysPressed[player].Add(key);
473 if (!isKeyPressed && !mInputs.KeysReleased[player].Contains(key))
474 mInputs.KeysReleased[player].Add(key);
478 public void ApplyMouseLocationInput(object playerIdentifier, int x, int y)
483 public void ApplyMouseButtonInput(object playerIdentifier, bool isButtonPressed)
488 public bool IsGameOver(object playerIdentifier)
493 public bool IsTerminated(object playerIdentifier)
498 public long Update(TimeSpan elapsedTime)
500 State.AdvanceFrame(mInputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state.
501 State.mDisplay.Update(elapsedTime, mThisPlayerID);
502 State.Entities.ForEach(delegate(IEntity e) { e.Update(elapsedTime); });
503 mInputs = new NextInputs(State.NumberOfPlayers); // Start with inputs cleared on the next frame.
504 //mDisplay.Update(elapsedTime);
505 return State.FrameNumber;
509 public long Draw(SpriteBatch spriteBatch)
511 bool allCharactersSelected = true;
512 for (int i = 0; i < State.NumberOfPlayers; i++)
514 //If player has not selected a player yet let them select one.
515 if (State.mCharacters[i] == null)
517 allCharactersSelected = false;
518 if (State.GetKeysDown(i).Contains(Keys.M))
520 State.mCharacters[i] = new Melee(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i);
521 State.mCharacters[i].LoadContent(mContentManager);
523 else if (State.GetKeysDown(i).Contains(Keys.R))
525 State.mCharacters[i] = new Ranged(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i);
526 State.mCharacters[i].LoadContent(mContentManager);
530 if (allCharactersSelected)
533 State.mDisplay.Draw(spriteBatch);
537 spriteBatch.GraphicsDevice.Clear(Color.Black);
538 spriteBatch.DrawString(menu, "Press R to select a Ranged Character and M to select a Melee Character", new Vector2(30, 30), Color.RosyBrown);
540 return CurrentFrameNumber;
546 #region Private Variables
550 ContentManager mContentManager;
553 Object[] mPlayerIdentifiers;