using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; namespace CarFire { /// /// Container class for the whole state of the game. /// public class GameState { #region Public Properties private const int hitMonsterScore = 20; private const int killMonsterScore = 100; public int HitMonsterScore { get { return hitMonsterScore; } } public int KillMonsterScore { get { return killMonsterScore; } } public long FrameNumber { get { return mFrameNumber; } } public long Checksum { get { return mChecksum; } } public int NumberOfPlayers { get { return mNumberOfPlayers; } } public Map Map; public List Entities = new List(); public List mProjectiles = new List(); public Player[] mCharacters; public Display mDisplay; public AI AIData; #endregion #region Public Methods /// /// Construct a game state container with the number of players. /// /// Number of players. public GameState(int numPlayers) { mNumberOfPlayers = numPlayers; mFrameNumber = 0; mCharacters = new Player[numPlayers]; mIsGameOver = new bool[numPlayers]; mIsTerminated = new bool[numPlayers]; mMouseLocation = new Point[numPlayers]; mMouseButton = new bool[numPlayers]; mKeysDown = new List[numPlayers]; for (int i = 0; i < numPlayers; i++) mKeysDown[i] = new List(); mKeypressCount = new int[numPlayers]; mElapsedTime = 0; mChecksum = 0; } /// /// Should be called by the Game class to advance the state /// to the next frame. /// /// The inputs that occurred to be /// applied this coming frame. /// Milliseconds; used for the checksum. public void AdvanceFrame(NextInputs inputs, long milliseconds) { mFrameNumber++; mElapsedTime += milliseconds; for (int player = 0; player < NumberOfPlayers; player++) { if (inputs.IsMousePressedChanged[player]) { mMouseButton[player] = inputs.MousePressed[player]; } if (inputs.IsMouseLocationChanged[player]) { mMouseLocation[player] = inputs.MouseLocation[player]; } foreach (Keys k in inputs.KeysPressed[player]) { if (!mKeysDown[player].Contains(k)) { mKeysDown[player].Add(k); mKeypressCount[player]++; } } foreach (Keys k in inputs.KeysReleased[player]) mKeysDown[player].Remove(k); } ComputeChecksum(); } /// /// Get the mouse location for a player. /// /// Player Number. /// Mouse location. public Point GetMouseLocation(int playerNum) { return mMouseLocation[playerNum]; } /// /// Get the mouse button state for a player. /// /// Player number. /// Mouse button state.. public bool GetMouseButton(int playerNum) { return mMouseButton[playerNum]; } /// /// Get the keyboard state for a player. /// /// Player number. /// Keyboard state. public List GetKeysDown(int playerNum) { return mKeysDown[playerNum]; } #endregion #region Private Methods // Calculates a checksum for debugging network synchronization issues. long ComputeChecksum() { mChecksum += FrameNumber; for (int i = 0; i < NumberOfPlayers; i++) { mChecksum = mChecksum + mKeypressCount[i]; mChecksum = mChecksum * 3 + (mIsGameOver[i] ? 1 : 2); mChecksum = mChecksum * 3 + (mIsTerminated[i] ? 1 : 2); foreach (Keys k in mKeysDown[i]) mChecksum = mChecksum * 257 + (int)k; mChecksum = mChecksum * 25789 + mMouseLocation[i].X * 259 + mMouseLocation[i].Y + 375; mChecksum = mChecksum * 3 + (mMouseButton[i] ? 1 : 2); } mChecksum += mElapsedTime; return mChecksum; } #endregion #region Private Variables int mNumberOfPlayers; public Point[] mMouseLocation; public bool[] mMouseButton; public List[] mKeysDown; long mFrameNumber; bool[] mIsGameOver; bool[] mIsTerminated; int[] mKeypressCount; long mElapsedTime; long mChecksum; #endregion } /// /// Container class for all the inputs for a single frame. /// public class NextInputs { public List[] KeysPressed; public List[] KeysReleased; public Point[] MouseLocation; public bool[] IsMouseLocationChanged; public bool[] MousePressed; public bool[] IsMousePressedChanged; public NextInputs(int numPlayers) { KeysPressed = new List[numPlayers]; KeysReleased = new List[numPlayers]; IsMouseLocationChanged = new bool[numPlayers]; MousePressed = new bool[numPlayers]; IsMousePressedChanged = new bool[numPlayers]; for (int i = 0; i < numPlayers; i++) KeysPressed[i] = new List(); for (int i = 0; i < numPlayers; i++) KeysReleased[i] = new List(); } } /// /// The big kahuna. /// public class Game : IDeterministicGame { #region Public Properties /// /// Get the content manager associated with this game. /// public ContentManager ContentManager { get { return mContentManager; } } /// /// Get the state. /// public GameState State; public bool[,] Grid { get { bool[,] grid = State.Map.Grid; foreach (IEntity entity in State.Entities) { Point coordinates = entity.Coordinates; if (State.Map.IsCellOpen(coordinates)) grid[coordinates.X, coordinates.Y] = false; } return grid; } } #endregion #region Public Methods public IEntity GetEntityAtCoordinates(Point point) { foreach (IEntity entity in State.Entities) { if (entity.Coordinates == point) return entity; } return null; } public Player GetPlayerAtCoordinates(Point point) { foreach (Player player in State.mCharacters) { if (player != null && player.Coordinates == point) return player; } return null; } public bool IsCellOpen(Point point) { if (!State.Map.IsCellOpen(point)) return false; if (GetEntityAtCoordinates(point) != null) return false; return true; } public Game() { } /// /// This method should be called whenever the players want to move to a new map. /// Not implemented yet. Need some way to get next map. /// public void startNewMap() { //TODO somehow get next map State.Entities.Clear(); //State.Map = State.Map.getNextMap(); for (int i = 0; i < State.mCharacters.Length; i++) { State.mCharacters[i].Coordinates = State.Map.GetStartingPositionForPlayer(i + 1); } State.Entities = State.Map.GetAllEntities(this); } public void LoadContent(ContentManager contentManager) { mContentManager = contentManager; menu = mContentManager.Load("menuFont"); } public void UnloadContent() { } private int GetPlayerNumber(Object playerIdentifier) { for (int i = 0; i < mPlayerIdentifiers.Length; i++) { if (mPlayerIdentifiers[i] == playerIdentifier) return i; } throw new Exception("Illegal player identifier" + playerIdentifier); } public Vector2 PreferredScreenSize { get { return new Vector2(800, 600); } } public int MinimumSupportedPlayers { get { return 1; } } public int MaximumSupportedPlayers { get { return 4; } } public void ResetGame(object[] playerIdentifiers, object thisPlayer) { int numPlayers = playerIdentifiers.Count(); mPlayerIdentifiers = new object[numPlayers]; for (int i = 0; i < numPlayers; i++) mPlayerIdentifiers[i] = playerIdentifiers[i]; mThisPlayerID = GetPlayerNumber(thisPlayer); State = new GameState(numPlayers); mInputs = new NextInputs(numPlayers); State.mDisplay = new Display(this); State.mDisplay.LoadContent(mContentManager); Texture2D mapTiles = mContentManager.Load("graphics/wallAndFloorTilesNoEdgeScale"); Tilemap tilemap = new Tilemap(mapTiles, 10, 7); tilemap.SetTile(' ', new Point(4, 5), TileFlags.Default); tilemap.SetTile('`', new Point(0, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('~', new Point(1, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('!', new Point(3, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('@', new Point(4, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('#', new Point(5, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('$', new Point(6, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('%', new Point(8, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('^', new Point(9, 1), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('&', new Point(0, 2), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('=', new Point(1, 2), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('*', new Point(2, 2), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('(', new Point(4, 2), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile(')', new Point(0, 3), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('_', new Point(2, 3), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile('-', new Point(9, 3), TileFlags.Closed | TileFlags.Wall); tilemap.SetTile(',', new Point(1, 4), TileFlags.Default); tilemap.SetTile('+', new Point(2, 4), TileFlags.Default); tilemap.SetTile('[', new Point(3, 4), TileFlags.Default); tilemap.SetTile(']', new Point(4, 4), TileFlags.Default); tilemap.SetTile('{', new Point(5, 4), TileFlags.Default); tilemap.SetTile('}', new Point(6, 4), TileFlags.Default); tilemap.SetTile('\\', new Point(8, 4), TileFlags.Default); tilemap.SetTile('|', new Point(9, 4), TileFlags.Default); tilemap.SetTile(';', new Point(0, 5), TileFlags.Default); tilemap.SetTile(':', new Point(1, 5), TileFlags.Default); tilemap.SetTile('\'', new Point(2, 5), TileFlags.Default); tilemap.SetTile('"', new Point(3, 5), TileFlags.Default); tilemap.SetTile('.', new Point(5, 5), TileFlags.Default); tilemap.SetTile('<', new Point(6, 5), TileFlags.Default); tilemap.SetTile('>', new Point(7, 5), TileFlags.Default); tilemap.SetTile('/', new Point(8, 5), TileFlags.Default); tilemap.SetTile('?', new Point(9, 5), TileFlags.Default); State.Map = mContentManager.Load("Maps/level1"); State.Map.Tilemap = tilemap; State.Entities = State.Map.GetAllEntities(this); State.AIData = new AI(this); /* mPlayers.Clear(); for (int i = 0; i < PlayerIdentifiers.Length; i++) { Human player = new Human(mMap, ""); mPlayers.Add(player); mDisplay.AddCharacters(player); mPlayers.Add(player); mDisplay.AddCharacters(player); } this.playerIdentifiers = PlayerIdentifiers; for (int i = 0; i < mPlayers.Count; i++) { Point starting = mMap.GetStartingPositionForPlayer(i + 1); mPlayers[i].Spawn(new Vector2(starting.X, starting.Y)); } */ } public long CurrentFrameNumber { get { return State.FrameNumber; } } public long CurrentChecksum { get { return 0; } } public void ApplyKeyInput(object playerIdentifier, Keys key, bool isKeyPressed) { //code from Prof Jensen's TestHarness int player = GetPlayerNumber(playerIdentifier); if (isKeyPressed && !mInputs.KeysPressed[player].Contains(key)) mInputs.KeysPressed[player].Add(key); if (!isKeyPressed && !mInputs.KeysReleased[player].Contains(key)) mInputs.KeysReleased[player].Add(key); } public void ApplyMouseLocationInput(object playerIdentifier, int x, int y) { } public void ApplyMouseButtonInput(object playerIdentifier, bool isButtonPressed) { } public bool IsGameOver(object playerIdentifier) { return false; } public bool IsTerminated(object playerIdentifier) { return false; } public long Update(TimeSpan elapsedTime) { State.AdvanceFrame(mInputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state. State.mDisplay.Update(elapsedTime, mThisPlayerID); State.Entities.ForEach(delegate(IEntity e) { e.Update(elapsedTime); }); mInputs = new NextInputs(State.NumberOfPlayers); // Start with inputs cleared on the next frame. //mDisplay.Update(elapsedTime); return State.FrameNumber; } public long Draw(SpriteBatch spriteBatch) { bool allCharactersSelected = true; for (int i = 0; i < State.NumberOfPlayers; i++) { //If player has not selected a player yet let them select one. if (State.mCharacters[i] == null) { allCharactersSelected = false; if (State.GetKeysDown(i).Contains(Keys.M)) { State.mCharacters[i] = new Melee(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i); State.mCharacters[i].LoadContent(mContentManager); } else if (State.GetKeysDown(i).Contains(Keys.R)) { State.mCharacters[i] = new Ranged(this, "", State.Map.GetStartingPositionForPlayer(i + 1), i); State.mCharacters[i].LoadContent(mContentManager); } } } if (allCharactersSelected) { State.mDisplay.Draw(spriteBatch); } else { spriteBatch.GraphicsDevice.Clear(Color.Black); spriteBatch.DrawString(menu, "Press R to select a Ranged Character and M to select a Melee Character", new Vector2(30, 30), Color.RosyBrown); } return CurrentFrameNumber; } #endregion #region Private Variables SpriteFont menu; ContentManager mContentManager; NextInputs mInputs; Object[] mPlayerIdentifiers; int mThisPlayerID; #endregion } }