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 = (bool[,])State.Map.Grid.Clone();
foreach (IEntity entity in State.Entities)
{
Point coordinates = entity.Coordinates;
grid[coordinates.X, coordinates.Y] = false;
}
foreach (Player player in State.mCharacters)
{
if (player == null) continue;
Point coordinates = player.Coordinates;
grid[coordinates.X, coordinates.Y] = false;
}
return grid;
}
}
#endregion
#region Public Methods
///
/// Get an entity at a certain place on the map.
///
/// The coordinates.
/// The entity, or null if none is at that location.
public IEntity GetEntityAtCoordinates(Point point)
{
foreach (IEntity entity in State.Entities)
{
if (entity.Coordinates == point) return entity;
}
return null;
}
///
/// Get a player at a certain place on the map.
///
/// The coordinates.
/// The player, or null if none is at that location.
public Player GetPlayerAtCoordinates(Point point)
{
foreach (Player player in State.mCharacters)
{
if (player != null && player.Coordinates == point) return player;
}
return null;
}
///
/// Determine if a cell is open, depending on the static scenery
/// of the map and if there are any collidable entities.
///
/// The coordinates.
/// True if the cell is open; false otherwise.
public bool IsCellOpen(Point point)
{
if (!State.Map.IsCellOpen(point)) return false;
IEntity entity = GetEntityAtCoordinates(point);
if (entity != null && entity.IsCollidable) return false;
Player player = GetPlayerAtCoordinates(point);
if (player != null) return false;
return true;
}
///
/// Remove a specific entity from the game. The entity can still
/// be tracked some other way, but it won't included when the game is
/// updating and drawing stuff.
///
/// The entity.
/// The entity that was removed, or null if no entity was removed.
public IEntity RemoveEntity(IEntity entity)
{
if (State.Entities.Remove(entity)) return entity;
return null;
}
///
/// Move on to the next map, and advance the level.
///
public void AdvanceLevel()
{
// TODO: Load the next map, etc...
}
///
/// Restart the current level.
///
public void Reset()
{
State.Map.Reset();
// TODO: Do other stuff to reset everything.
}
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);
// Load the tilemap.
Texture2D mapTiles = mContentManager.Load("graphics/wallAndFloorTilesNoEdgeScale");
Tilemap tilemap = new Tilemap(mapTiles, 10, 7);
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(3, 2), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('#', new Point(4, 2), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('$', new Point(5, 2), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('%', new Point(6, 2), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('^', new Point(8, 2), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('&', new Point(9, 2), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('*', new Point(0, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('(', new Point(1, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile(')', new Point(2, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('-', new Point(3, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('=', new Point(4, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('_', new Point(5, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('+', new Point(6, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('|', new Point(8, 3), TileFlags.Closed | TileFlags.Wall);
tilemap.SetTile('[', new Point(0, 4), TileFlags.Default);
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(7, 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(4, 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);
Map.Tilemap = tilemap;
// Load the first map.
State.Map = mContentManager.Load