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
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;
#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
private const int hitMonsterScore = 20;
private const int killMonsterScore = 100;
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...
//TODO somehow get next map
State.Entities.Clear();
String nextMap = State.Map.Next;
State.Map = mContentManager.Load