using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace CS_3505_Project_06.CS_3505 { // Everything in objects built from this class represent the critical game state public class GameState { public long frameNumber; private long checksum; public long Checksum { get { return checksum; } } public bool[] isGameOver; public bool[] isTerminated; // Since this is not a game, I'll just keep track of the user inputs as the game state. public int[] mouseLocationX; public int[] mouseLocationY; public bool[] mouseButton; public List[] keysDown; public int[] keypressCount; public long elapsedTime; /* Constructor */ public GameState() { frameNumber = 0; checksum = 0; isGameOver = new bool[4]; isTerminated = new bool[4]; mouseLocationX = new int[4]; mouseLocationY = new int[4]; mouseButton = new bool[4]; keysDown = new List[4]; for (int i = 0; i < 4; i++) keysDown[i] = new List(); keypressCount = new int[4]; elapsedTime = 0; checksum = 0; } /* The game engine! */ public void advanceFrame(NextInputs inputs, long milliseconds) { // Advance frame number frameNumber++; // Advance game - for the test harness, just record statistics and input states. elapsedTime += milliseconds; for (int player = 0; player < 4; player++) { if (isGameOver[player]) continue; if (inputs.mousePressedChanged[player]) mouseButton[player] = inputs.mousePressed[player]; if (inputs.mouseLocationChanged[player]) { mouseLocationX[player] = inputs.mouseLocationX[player]; mouseLocationY[player] = inputs.mouseLocationY[player]; } foreach (Keys k in inputs.keysPressed[player]) if (!keysDown[player].Contains(k)) { keysDown[player].Add(k); keypressCount[player]++; } foreach (Keys k in inputs.keysReleased[player]) keysDown[player].Remove(k); // If the mouse was pressed for a certain player, activate game over or terminated states as appropriate if (inputs.mousePressed[player]) for (int p = 0; p < 4; p++) { int x = 200 * p + 10; int y = 220; if (mouseLocationX[player] >= x && mouseLocationY[player] >= y && mouseLocationX[player] < x + 25 && mouseLocationY[player] < y + 25) { isGameOver[p] = true; } y += 25; if (mouseLocationX[player] >= x && mouseLocationY[player] >= y && mouseLocationX[player] < x + 25 && mouseLocationY[player] < y + 25) { isGameOver[p] = true; isTerminated[p] = true; } } } // Advance the checksum. computeChecksum(); } /* Just hash the values */ private long computeChecksum() { checksum += frameNumber; for (int i = 0; i < 4; i++) { checksum = checksum + keypressCount[i]; checksum = checksum * 3 + (isGameOver[i] ? 1 : 2); checksum = checksum * 3 + (isTerminated[i] ? 1 : 2); foreach (Keys k in keysDown[i]) checksum = checksum * 257 + (int) k; checksum = checksum * 25789 + mouseLocationX[i] * 259 + mouseLocationY[i] + 375; checksum = checksum * 3 + (mouseButton[i] ? 1 : 2); } checksum += elapsedTime; return checksum; } } // This class encapsulates inputs from the players. public class NextInputs { public List[] keysPressed; public List[] keysReleased; public int[] mouseLocationX; public int[] mouseLocationY; public bool[] mouseLocationChanged; public bool[] mousePressed; public bool[] mousePressedChanged; public NextInputs() { keysPressed = new List[4]; keysReleased = new List[4]; mouseLocationX = new int[4]; mouseLocationY = new int[4]; mouseLocationChanged = new bool[4]; mousePressed = new bool[4]; mousePressedChanged = new bool[4]; for (int i = 0; i < 4; i++) keysPressed[i] = new List(); for (int i = 0; i < 4; i++) keysReleased[i] = new List(); } } public class TestHarness : IDeterministicGame { GameState state; NextInputs inputs; Object[] playerIdentifiers; int thisPlayerID; // Instance variables here contribute to the display, but not the game state Texture2D crosshair; SpriteFont font; // Constructor public TestHarness() { playerIdentifiers = new Object[4]; } // Helper methods private int idPlayer(Object playerIdentifier) { for (int i = 0; i < playerIdentifiers.Length; i++) if (playerIdentifiers[i] == playerIdentifier) return i; throw new Exception("Illegal player identifier" + playerIdentifier); } // Implementation of the DeterministicGame interface #region IDeterministicGame Members public void LoadContent(ContentManager contentManager) { crosshair = contentManager.Load("CS 3505/Crosshair"); font = contentManager.Load("CS 3505/GameFont"); } public void UnloadContent() { // Nothing to do - the content manager will take care of it. } public Vector2 PreferredScreenSize { get { return new Vector2(800, 600); } } public int MinimumSupportedPlayers { get { return 4; } } public int MaximumSupportedPlayers { get { return 4; } } public void ResetGame(Object[] playerIdentifiers, Object thisPlayer) { //if (playerIdentifiers.Length != 4) // throw new Exception("This game requires four players."); // Copy the player identifiers - do not rely on the array parameter not changing. // Now the test harness will at least run with less than 4 players... int numPlayers = playerIdentifiers.Count(); for (int i = 0; i < numPlayers; i++) this.playerIdentifiers[i] = playerIdentifiers[i]; // Create new game state and inputs objects. state = new GameState(); inputs = new NextInputs(); // Record 'this' player. this.thisPlayerID = idPlayer(thisPlayer); } public long CurrentFrameNumber { get { return state.frameNumber; } } public long CurrentChecksum { get { return state.Checksum; } } public void ApplyKeyInput(Object playerIdentifier, Keys key, bool isKeyPressed) { int player = idPlayer(playerIdentifier); if (isKeyPressed && !inputs.keysPressed[player].Contains(key)) inputs.keysPressed[player].Add(key); if (!isKeyPressed && !inputs.keysReleased[player].Contains(key)) inputs.keysReleased[player].Add(key); } public void ApplyMouseLocationInput(Object playerIdentifier, int x, int y) { int player = idPlayer(playerIdentifier); inputs.mouseLocationX[player] = x; inputs.mouseLocationY[player] = y; inputs.mouseLocationChanged[player] = true; } public void ApplyMouseButtonInput(Object playerIdentifier, bool isButtonPressed) { int player = idPlayer(playerIdentifier); inputs.mousePressed[player] = isButtonPressed; inputs.mousePressedChanged[player] = true; } public bool IsGameOver(Object playerIdentifier) { int player = idPlayer(playerIdentifier); return state.isGameOver[player] ; } public bool IsTerminated(object playerIdentifier) { int player = idPlayer(playerIdentifier); return state.isTerminated[player]; } public long Update(TimeSpan elapsedTime) { state.advanceFrame(inputs, elapsedTime.Milliseconds); // Apply the inputs, advance game state. inputs = new NextInputs(); // Start with inputs cleared on the next frame. return state.frameNumber; } public long Draw(SpriteBatch spriteBatch) { centerString(spriteBatch, Color.White, "CS 3505 - Software Practice 2", 0, 800, 0); centerString(spriteBatch, Color.White, "Test Harness", 0, 800, 25); centerString(spriteBatch, Color.White, "Debug output", 0, 800, 50); nameIntPair(spriteBatch, Color.White, "Frame:", state.frameNumber, 10, 150, 100); nameHexPair(spriteBatch, Color.White, "Checksum:", state.Checksum, 215, 515, 100); nameDecPair(spriteBatch, Color.White, "Elapsed Time:", state.elapsedTime / 1000.0f, 570, 790, 100); Console.WriteLine("Frame: " + state.frameNumber + " Checksum: " + state.Checksum); printPlayer(spriteBatch, Color.Turquoise, 0, 10, 190, 170); printPlayer(spriteBatch, Color.Wheat, 1, 210, 390, 170); printPlayer(spriteBatch, Color.Tomato, 2, 410, 590, 170); printPlayer(spriteBatch, Color.Violet, 3, 610, 790, 170); if (!state.isGameOver[0]) spriteBatch.Draw(crosshair, new Vector2(state.mouseLocationX[0] - 5, state.mouseLocationY[0] - 5), Color.Turquoise); if (!state.isGameOver[1]) spriteBatch.Draw(crosshair, new Vector2(state.mouseLocationX[1] - 5, state.mouseLocationY[1] - 5), Color.Wheat); if (!state.isGameOver[2]) spriteBatch.Draw(crosshair, new Vector2(state.mouseLocationX[2] - 5, state.mouseLocationY[2] - 5), Color.Tomato); if (!state.isGameOver[3]) spriteBatch.Draw(crosshair, new Vector2(state.mouseLocationX[3] - 5, state.mouseLocationY[3] - 5), Color.Violet); spriteBatch.Draw(crosshair, new Vector2(Mouse.GetState().X - 5, Mouse.GetState().Y - 5), Color.White); return state.frameNumber; } #endregion void printPlayer(SpriteBatch spriteBatch, Color c, int player, float left, float right, float top) { leftJustify(spriteBatch, c, String.Format("Player {0}", player+1), left, top); top += 10; leftJustify(spriteBatch, c, "_________", left, top); top += 40; nameIntPair(spriteBatch, c, "[X] Game Over", state.isGameOver[player]?1:0, left, right, top); top += 25; nameIntPair(spriteBatch, c, "[X] Terminated", state.isTerminated[player] ? 1 : 0, left, right, top); top += 40; nameIntPair(spriteBatch, c, "Mouse X", (int)state.mouseLocationX[player], left, right, top); top += 25; nameIntPair(spriteBatch, c, "Mouse Y", (int)state.mouseLocationY[player], left, right, top); top += 40; leftJustify(spriteBatch, c, "Mouse", left, top); rightJustify(spriteBatch, c, state.mouseButton[player]?"Pressed":"Released", right, top); top += 40; nameIntPair(spriteBatch, c, "Key count", (int)state.keypressCount[player], left, right, top); top += 25; leftJustify(spriteBatch, c, "Keys", left, top); if (state.keysDown[player].Count == 0) rightJustify(spriteBatch, c, "None", right, top); else foreach (Keys k in state.keysDown[player]) { rightJustify(spriteBatch, c, k.ToString(), right, top); top += 25; } } void centerString(SpriteBatch spriteBatch, Color c, String s, float left, float right, float top) { Vector2 v = font.MeasureString(s); float x = left + (right-left - v.X) / 2; float y = top; spriteBatch.DrawString(font, s, new Vector2(x, y), c); } void centerString(SpriteBatch spriteBatch, Color c, String s, Rectangle r) { Vector2 v = font.MeasureString(s); float x = r.Left + (r.Width - v.X) / 2; float y = r.Top + (r.Height - v.Y) / 2; spriteBatch.DrawString(font, s, new Vector2(x, y), c); } void leftJustify(SpriteBatch spriteBatch, Color c, String s, float left, float top) { float x = left; float y = top; spriteBatch.DrawString(font, s, new Vector2(x, y), c); } void rightJustify(SpriteBatch spriteBatch, Color c, String s, float right, float top) { Vector2 v = font.MeasureString(s); float x = right - v.X; float y = top; spriteBatch.DrawString(font, s, new Vector2(x, y), c); } void nameDecPair(SpriteBatch spriteBatch, Color c, String name, float number, float left, float right, float top) { String num = String.Format("{0:.00}", number); leftJustify(spriteBatch, c, name, left, top); rightJustify(spriteBatch, c, num, right, top); } void nameIntPair(SpriteBatch spriteBatch, Color c, String name, long number, float left, float right, float top) { String num = String.Format("{0}", number); leftJustify(spriteBatch, c, name, left, top); rightJustify(spriteBatch, c, num, right, top); } void nameHexPair(SpriteBatch spriteBatch, Color c, String name, long number, float left, float right, float top) { String num = String.Format("{0:x}", number); leftJustify(spriteBatch, c, name, left, top); rightJustify(spriteBatch, c, num, right, top); } } }