using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework; using Util; namespace FaceOff { class Board : GameObject { SoundManager sound; private int mRows; private int mCols; public int Rows { get { return mRows; } set { mRows = value; } } public int Columns { get { return mCols; } set { mCols = value; } } private bool validatingBoard = false; public bool ValidatingBoard { get { return validatingBoard; } set { validatingBoard = value; } } private float jitter; private int convertJitter; private float validateTime; private int cellSpace = 0; private int boardWidth = 0; private int boardHeight = 0; private int viewWidth; private int viewHeight; private int borderWidth; private int numCells; private Logger mLogger; private Texture2D blankTex; private Texture2D mPixel; private Texture2D StarTex; private Group currentGroup = null; private List mCells; private List undoGroup; private List trayGroup; private List validationGroups; private List groups; public List Groups { get { return groups; } } private Cell[,] cells; private int lineWidth = 0; public Board(int rows, int cols, int cellSize, SoundManager s, Logger logger) { sound = s; mLogger = logger; mRows = rows; mCols = cols; viewWidth = 800; viewHeight = 600; numCells = mRows * mCols; cellSpace = cellSize; mCells = new List(); cells = new Cell[rows,cols]; groups = new List(); undoGroup = new List(); trayGroup = new List(); } public void initialize() { lineWidth = mPixel.Width; boardWidth = cellSpace * mCols; boardHeight = cellSpace * mRows; borderWidth = (viewWidth - boardWidth) / 2; cells = new Cell[Rows, Columns]; groups = new List(); } public void LoadContent(ContentManager content) { mPixel = content.Load(@"Texture\pixel"); blankTex = content.Load(@"Texture\Blank"); StarTex = content.Load(@"Texture\Star"); } public void GenerateBoard() { //create Cells and add them to a list for (int r = 0; r < mRows; ++r) { for (int c = 0; c < mCols; ++c) { //this needs to be randomized Cell tempCell = new Cell(); tempCell.Position = new Vector2(r, c); tempCell.Texture = blankTex; tempCell.Visible = true; tempCell.Blank = false; tempCell.Size = cellSpace - lineWidth; //set the position that the image will be drawn int x = lineWidth + borderWidth + (cellSpace * c) - 1; int y = GameConfig.VerticalOffsetBoard + (cellSpace * r); tempCell.DrawPosition = new Vector2(x,y); tempCell.Star = new BorderStar(tempCell.DrawPosition, tempCell.Size, tempCell.Size); tempCell.Star.Texture = StarTex; //scale the image to fit the cell tempCell.Scale(); //set the collsion box size tempCell.CollisionBox = new BoundingBox(new Vector3(x,y,-5), new Vector3((x+cellSpace),(y+cellSpace),5)); //add cell to the list of cells mCells.Add(tempCell); cells[r,c] = tempCell; } } randomizeBoard(); } private void randomizeBoard() { //a random cell int cellRow; int cellCol; //a list of categories that can be added to the board, no duplicates List categories = new List(); for (int i = 0; i < FaceLoader.NumCategories; ++i) { categories.Add(i); } //a random category to select from the list int category; //a random number to choose a blank for each group int blankNum; //flag for either vertical(1) or horizontal(2) int direction; Cell tempCell; Cell neighborCellOne = null; Cell neighborCellTwo = null; Group tempGroup; bool valid = false; //set groups on the board based on how many cells in the facetray for (int i = 0; i < GameConfig.FaceTraySize; ++i ) { //choose a random number which represents a cell //continue choosing until the cell was not yet set do{ //choose a random direction direction = Util.Random.Next(2); valid = false; cellRow = Util.Random.Next(Rows); cellCol = Util.Random.Next(Columns); tempCell = cells[cellRow,cellCol]; switch (direction) { case 0: if((cellRow-1) >= 0 && (cellRow+1) < Rows ) { neighborCellOne = cells[cellRow-1,cellCol]; neighborCellTwo = cells[cellRow+1,cellCol]; if(neighborCellOne.Category < 0 && neighborCellTwo.Category < 0) { valid = true; } } break; case 1: if((cellCol-1) >= 0 && (cellCol+1) < Columns ) { neighborCellOne = cells[cellRow,cellCol-1]; neighborCellTwo = cells[cellRow,cellCol+1]; if(neighborCellOne.Category < 0 && neighborCellTwo.Category < 0) { valid = true; } } break; } }while( tempCell.Category >= 0 || valid == false); tempGroup = new Group(); //select a category randomly from the available categories and remove the category from the list of available int index = Util.Random.Next(categories.Count); category = (int)categories.ElementAt(index); categories.RemoveAt(index); tempGroup.CellOne = tempCell; tempGroup.CellTwo = neighborCellOne; tempGroup.CellThree = neighborCellTwo; tempGroup.Category = category; blankNum = Util.Random.Next(1, 4); tempGroup.setBlank(blankNum, blankTex); Texture2D tex1 = FaceLoader.GetRandomTexture(category); Texture2D tex2 = FaceLoader.GetRandomTexture(category); tempGroup.setTextures(tex1, tex2); convertJitter = (int)(GameConfig.ValidateJitter * 10f); jitter = Util.Random.Next(convertJitter) * 0.1f; tempGroup.ApplyJitter(jitter); groups.Add(tempGroup); } //the category of the last cell placed int lastCategory = 0; //fill in the rest of the board for (int r = 0; r < mRows; ++r) { for (int c = 0; c < mCols; ++c) { tempCell = cells[r,c]; if (tempCell.Category < 0) { //make sure that the last category does not match the current category so that //the board is less confusing do { category = Util.Random.Next(FaceLoader.NumCategories); } while (lastCategory == category); lastCategory = category; tempCell.Category = category; tempCell.Placed = true; tempCell.Texture = FaceLoader.GetRandomTexture(category); tempCell.Scale(); } } } } public int checkClick(Vector2 clickPos) { Cell cell; //the cell reference number so that it can be replaced in the facetray int retVal = 0; for (int r = 0; r < mRows; ++r) { for (int c = 0; c < mCols; ++c) { cell = cells[r, c]; //if the blank cell has been filled, then check it for collision with mouse if( cell.HoldingReference != 0 && cell.Visible == true ){ //check cell collision with mouse click position if (cell.CollisionBox.Contains(new Vector3(clickPos.X, clickPos.Y, 0)) == ContainmentType.Contains) { retVal = cell.HoldingReference; cell.HoldingReference = 0; cell.Texture = blankTex; cell.Blank = true; cell.Placed = false; cell.AnimatingStar = false; cell.Scale(); mLogger.Write((int)EventCode.FO_BOARDUNDO, typeof(EventCode), "X="+clickPos.X+"_Y="+clickPos.Y); //return the reference cell number so that we dont have to continue //iterating through the cells knowing that we already found the one that was clicked. return retVal; } } } } //no cell was clicked to be undone return retVal; } public bool checkRelease( Vector2 mousePos, TrayCell trayCell ) { bool retVal = false; Cell cell; for (int r = 0; r < mRows; ++r) { for (int c = 0; c < mCols; ++c) { cell = cells[r, c]; //if the cell is a blank cell, check it for collision with the mouse drop position if (cell.Blank) { #if DEBUG Console.WriteLine("Cell Position:" + cell.Position); #endif if (cell.CollisionBox.Contains(new Vector3(mousePos.X, mousePos.Y, 0)) == ContainmentType.Contains) { mLogger.Write((int)EventCode.FO_BOARDFILL, typeof(EventCode), "X=" + mousePos.X + "_Y=" + mousePos.Y); cell.contains(trayCell); retVal = true; //this is a preformance enhancement because we dont need to continue checking //collisions when we find one. return retVal; } } } } return retVal; } public void validateBoard() { validatingBoard = true; validationGroups = new List(groups); } public List getUndoGroup() { return undoGroup; } public void clearUndoGroup() { undoGroup = new List(); } private void undoPlacement(Group group) { if (group.CellOne.HoldingReference != 0) { undoGroup.Add(group.CellOne.HoldingReference); group.CellOne.HoldingReference = 0; group.CellOne.Texture = blankTex; group.CellOne.Blank = true; group.CellOne.Placed = false; group.CellOne.AnimatingStar = false; group.CellOne.Scale(); } else if (group.CellTwo.HoldingReference != 0) { undoGroup.Add(group.CellTwo.HoldingReference); group.CellTwo.HoldingReference = 0; group.CellTwo.Texture = blankTex; group.CellTwo.Blank = true; group.CellTwo.Placed = false; group.CellTwo.AnimatingStar = false; group.CellTwo.Scale(); } else if (group.CellThree.HoldingReference != 0) { undoGroup.Add(group.CellThree.HoldingReference); group.CellThree.HoldingReference = 0; group.CellThree.Texture = blankTex; group.CellThree.Blank = true; group.CellThree.Placed = false; group.CellThree.AnimatingStar = false; group.CellThree.Scale(); } //set a new validate time with random jitter convertJitter = (int)(GameConfig.ValidateJitter * 10f); jitter = Util.Random.Next(convertJitter) * 0.1f; group.ApplyJitter(jitter); } public List getTrayGroup() { return trayGroup; } public void clearTrayGroup() { trayGroup = new List(); } private void removeFromTray(Group group) { if (group.CellOne.HoldingReference != 0) { trayGroup.Add(group.CellOne.HoldingReference); } else if (group.CellTwo.HoldingReference != 0) { trayGroup.Add(group.CellTwo.HoldingReference); } else if (group.CellThree.HoldingReference != 0) { trayGroup.Add(group.CellThree.HoldingReference); } } void DrawBoard(SpriteBatch spriteBatch) { for (int i = 0; i < (mRows + 1); ++i) { DrawLine(spriteBatch, new Vector2(borderWidth, GameConfig.VerticalOffsetBoard + (cellSpace * i)), new Vector2(viewWidth - borderWidth, GameConfig.VerticalOffsetBoard + (cellSpace * i)), GameConfig.FaceOffGreen); } for (int i = 0; i < (mCols + 1); ++i) { DrawLine(spriteBatch, new Vector2(borderWidth + (cellSpace * i), GameConfig.VerticalOffsetBoard - lineWidth), new Vector2(borderWidth + (cellSpace * i), boardHeight + GameConfig.VerticalOffsetBoard), GameConfig.FaceOffGreen); } } void DrawLine(SpriteBatch spriteBatch, Vector2 a, Vector2 b, Color col) { Vector2 Origin = new Vector2(0.5f, 0.0f); Vector2 diff = b - a; float angle; Vector2 Scale = new Vector2(1.0f, diff.Length() / mPixel.Height); angle = (float)(Math.Atan2(diff.Y, diff.X)) - MathHelper.PiOver2; spriteBatch.Draw(mPixel, a, null, col, angle, Origin, Scale, SpriteEffects.None, 1.0f); } public void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { DrawBoard(spriteBatch); Cell cell; for (int r = 0; r < cells.GetLength(0); ++r) { for (int c = 0; c < cells.GetLength(1); ++c) { cell = cells[r, c]; if (cell != null) { cell.Draw(spriteBatch); } } } } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. public void Update(GameTime gameTime) { //if we need to validate the board if (validatingBoard) { //select a group if none is currently validating while( currentGroup == null && validationGroups.Count > 0){ currentGroup = validationGroups.ElementAt(0); validationGroups.RemoveAt(0); //if the current group was already made correct, then do not validate again //or if the group was not answered then do not validate if (currentGroup.Correct || !(currentGroup.isPlaced())) { currentGroup = null; } } if (currentGroup != null) { //subtract the time elapsed since the last update call from the //time the user needs to wait for the group to validate currentGroup.ValidateTime -= ((gameTime.ElapsedGameTime.Milliseconds) * 0.001f); //if the wait time is over then validate the group if (currentGroup.ValidateTime <= 0) { //check to see if the group is correct if (currentGroup.checkCorrect()) { //do an animation to remove group from the board removeFromTray(currentGroup); mLogger.Write((int)EventCode.FO_CORRECT_GROUP, typeof(EventCode), FaceLoader.CategoryNames[currentGroup.Category]); //SoundManager.SoundManager.PlayEffect("right"); sound.PlaySound("right"); } else { //make the filled cell blank again undoPlacement(currentGroup); mLogger.Write((int)EventCode.FO_INCORRECT_GROUP, typeof(EventCode), FaceLoader.CategoryNames[currentGroup.Category]); sound.PlaySound("wrong"); //SoundManager.SoundManager.PlayEffect("wrong"); } //set the currentgroup to null so that we can get the next group currentGroup = null; } } else { //if the currentGroup is null then we are done validating the board validatingBoard = false; } } Cell cell; for (int r = 0; r < cells.GetLength(0); ++r) { for (int c = 0; c < cells.GetLength(1); ++c) { cell = cells[r, c]; if (cell != null) { cell.Update(gameTime); } } } } } }