using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Globalization; using Brains.Framework.QuadTree; using Brains.Framework.Map; using Brains.Framework.Utility; using Brains.Framework.Behaviors.PathFinding; using Brains.Framework.Grouping; using Brains.Spatial; namespace Brains.Framework { /// /// Class represents a game World for the AI Engine. /// Contains Grids and Actors /// public class World { // //privates // private List _agentsToRemove = new List(); private Texture2D _mapTexture; private WorldMap _map; internal SpatialTable _spatialtable; // //Public properties // /// /// Parent Engine /// public AIEngine Engine { get; set; } /// /// Gets or sets the list of Actors in the world /// public List Actors = new List(); public Dictionary Groups { get; set; } /// /// Gets the world map /// public WorldMap Map { get { return _map; } } public World() { Groups = new Dictionary(); } // //Public Methods // /// /// This method calls Update on all Actors. This can be overriden. /// /// The GameTime elapsed since the last update public virtual void Update(GameTime gameTime) { foreach (var item in Actors) { item.UpdatePerception(gameTime); } foreach (var item in Actors) { item.Update(gameTime); } if (_agentsToRemove.Count > 0) { for (int i = 0; i < _agentsToRemove.Count; i++) { Actors.Remove(_agentsToRemove[i]); } _agentsToRemove.Clear(); } } /// /// Initializes a new world map with the provides parameters /// /// The width of the map in pixels /// The height of the map in pixels /// The size of a cells width or height public void SetupMap(int width, int height, int cellsize) { SetupMap(width, height, cellsize, width / cellsize, height / cellsize,typeof(Grid)); } protected static sbyte[,] direction = new sbyte[4, 2] { { 0,-1} , { 1, 0}, { 0, 1}, {-1, 0} }; /// /// This method will create a game Map of the defined size and automatically cluster the map into grids /// /// The full width in pixels of the map /// The full height in pixels of the map /// The width or height of a gridcell /// Maximum number of columns in any grid of the cluster /// internal void SetupMap(int width, int height, int cellsize, int maxclusterwidth,int maxclusterheight,Type gridType) { _map = new WorldMap(width,height); int totalCols = width / cellsize; int totalRows= height / cellsize; int clusterRows = totalCols / maxclusterwidth; int clusterCols = totalRows/ maxclusterheight; _map.ClusterGrid.Setup(clusterRows, clusterCols); _spatialtable = new SpatialTable(); _spatialtable.Setup(width, height, cellsize); Vector2 position = new Vector2(); for (int y = 0; y < clusterRows; y++) { for (int x = 0; x < clusterCols; x++) { Grid grid = (Grid)gridType.Assembly.CreateInstance(gridType.FullName); grid.Setup(position, maxclusterwidth * cellsize, maxclusterheight * cellsize, cellsize); //Grid grid = (Grid)gridType.Assembly.CreateInstance( // gridType.FullName, // true, // System.Reflection.BindingFlags.CreateInstance, // null, // new object[] { // position, // maxclusterwidth * cellsize, // maxclusterheight* cellsize, // cellsize }, // CultureInfo.CurrentCulture, // null); grid.Initialize(); position.X += maxclusterwidth * cellsize; Map.AddGrid(grid); } position.Y += maxclusterheight * cellsize; position.X = 0; } } public void AnotateMap() { List> list = new List>(); Map.GridCellTree.GetAllItems(ref list); //Grid grid = Map.Cluster[index]; //Annotate clearance. int rows = Map.ClusterGrid.Rows; int cols= Map.ClusterGrid.Cols; Cluster cluster = Map.ClusterGrid; for (int xx = cluster.TotalCols - 1; xx >= 0; xx--) { for (int yy = cluster.TotalRows - 1; yy >= 0; yy--) { //int x = xx / Map.Cluster[0].Cols; //int y = yy/ Map.Cluster[0].Rows; //int index = y * Map.Cols + x; //Grid grid = Map.Cluster[index]; int index = yy * cluster.TotalCols + xx; GridCell cell = Map.AllCells[index];// grid.GetCell(xx - (x * grid.Cols), yy - (y * grid.Rows)); if (cell.Type == 0) //If the cell is not traversable, bail out. continue; //Get adjacent tiles GridCell c1, c2, c3; int y = (index - (index % cluster.TotalCols)) / cluster.TotalCols; int x = index % cluster.TotalCols; int newindex = (y + 1) * cluster.TotalCols + (x + 1); c1 = null; c2 = null; c3 = null; if(Map.AllCells.Count > newindex) c1 = Map.AllCells[newindex];// grid.GetCell(cell.X + 1, cell.Y + 1); newindex = (y) * cluster.TotalCols + (x + 1); if (Map.AllCells.Count > newindex) c2 = Map.AllCells[newindex]; //grid.GetCell(cell.X + 1, cell.Y); newindex = (y + 1) * cluster.TotalCols + (x); if (Map.AllCells.Count > newindex) c3 = Map.AllCells[newindex]; //grid.GetCell(cell.X, cell.Y + 1); //If all tiles are valid if (c1 != null && c2 != null && c3 != null) { int min = c1.Labels[AIConsts.CLEARANCEVALUE]; min = c2.Labels[AIConsts.CLEARANCEVALUE] < min ? c2.Labels[AIConsts.CLEARANCEVALUE] : min; min = c3.Labels[AIConsts.CLEARANCEVALUE] < min ? c3.Labels[AIConsts.CLEARANCEVALUE] : min; cell.Labels[AIConsts.CLEARANCEVALUE] = min + 1; // NB: +1 for minimum tile clearance } else { for (int i = 1; i < 3; i++) { int v = i & cell.Type; if (v == cell.Type) cell.Labels[AIConsts.CLEARANCEVALUE] = 1; else cell.Labels[AIConsts.CLEARANCEVALUE] = 0; } //Otherwise it must be up against a non traversable tile. } } } if (cluster.Grids.Count > 1) { int index = 0; for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) { SetHorizontalEntrancesOnGrid(x, y); SetVerticalEntrancesOnGrid(x, y); ////Check right edge //if (cluster.Grids.Count > index + 1) //{ // for (int row = 0; row < cluster.Grids[index].Rows; row++) // { // GridCell leftcell = cluster.Grids[index].GetCell( // cluster.Grids[index].Cols - 1, // row); // GridCell rightcell = cluster.Grids[index + 1].GetCell( // 0, // row); // if (leftcell.Type != AIConsts.BLOCKED_TILE && // rightcell.Type != AIConsts.BLOCKED_TILE) // { // //leftcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // //rightcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // } // } //} ////check left edge //if (index > 1) //{ // for (int row = 0; row < cluster.Grids[index].Rows; row++) // { // GridCell leftcell = cluster.Grids[index - 1].GetCell( // cluster.Grids[index - 1].Cols - 1, // row); // GridCell rightcell = cluster.Grids[index].GetCell( // 0, // row); // if (leftcell.Type != AIConsts.BLOCKED_TILE && // rightcell.Type != AIConsts.BLOCKED_TILE) // { // //leftcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // //rightcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // } // } //} ////check bottom edge //if (index + cols < cluster.Grids.Count) //{ // for (int col = 0; col < cluster.Grids[index].Cols; col++) // { // GridCell topcell = cluster.Grids[index].GetCell( // col, // cluster.Grids[index].Rows - 1); // GridCell bottomcell = cluster.Grids[index + cols].GetCell( // col, // 0); // if (topcell.Type != AIConsts.BLOCKED_TILE && // bottomcell.Type != AIConsts.BLOCKED_TILE) // { // // bottomcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // // topcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // } // } //} ////Check Top //if (index - cols > 0) //{ // for (int col = 0; col < cluster.Grids[index].Cols; col++) // { // GridCell topcell = cluster.Grids[index - cols].GetCell( // col, // cluster.Grids[index - cols].Rows - 1); // GridCell bottomcell = cluster.Grids[index].GetCell( // col, // 0); // if (topcell.Type != AIConsts.BLOCKED_TILE && // bottomcell.Type != AIConsts.BLOCKED_TILE) // { // //bottomcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // //topcell.Labels[AIConsts.ISVALIDCLUSTEREDGE] = 1; // } // } //} index++; } } } //Precalculate paths for each grid transition int gridIndex = 0; foreach (var item in Map.ClusterGrid.Grids) { int y = (gridIndex - (gridIndex % Map.ClusterGrid.Cols)) / Map.ClusterGrid.Cols; int x = gridIndex % Map.ClusterGrid.Cols; //North To East if (y > 0 && x < Map.ClusterGrid.Cols - 1) { PredeterminePaths(item,x, y - 1, x + 1, y); } //East To South if (x < Map.ClusterGrid.Cols - 1 && y 0) { PredeterminePaths(item, x, y + 1, x - 1, y); } //West To Nort if (x>0 && y>0) { PredeterminePaths(item, x - 1, y, x, y - 1); } //West To East if (x > 0 && x < Map.ClusterGrid.Cols-1) { PredeterminePaths(item, x - 1, y, x + 1, y); } //North To South if (y > 0 && y < Map.ClusterGrid.Rows- 1) { PredeterminePaths(item, x, y - 1, x, y + 1); } gridIndex++; } //Cluster cl = Map.ClusterGrid; //int index1 = 0; //foreach (var item in Map.ClusterGrid.Grids) //{ // int y = (index1 - (index1 % cl.Cols)) / cl.Cols; // int x = index1 % cl.Cols; // //Top To Right // if (y > 0 && x < cl.Cols - 1) // { // int indexF = (y-1) * cl.Cols + x ; // int indexT = y * cl.Cols + (x+1); // GridCell from; // GridCell to; // from = GetFreeCellAt(item, Direction.up); // to = GetFreeCellAt(item, Direction.rigth); // if(from!=null&& to!=null) // CalculatePath(from, to,indexF,indexT); // } // //Right To Bottom // if (y < cl.Rows - 1 && x < cl.Cols - 1) // { // int indexF = y * cl.Cols + (x + 1); // int indexT = (y + 1) * cl.Cols + x; // GridCell from; // GridCell to; // from = GetFreeCellAt(item, Direction.rigth); // to = GetFreeCellAt(item, Direction.down); // if (from != null && to != null) // CalculatePath(from, to, indexF, indexT); // } // //Bottom To Left // if (y < cl.Rows - 1 && x > 0) // { // int indexF = (y + 1) * cl.Cols + x; // int indexT = y * cl.Cols + (x - 1); // GridCell from; // GridCell to; // from = GetFreeCellAt(item, Direction.down); // to = GetFreeCellAt(item, Direction.left); // if (from != null && to != null) // CalculatePath(from, to, indexF, indexT); // } // //Left To Top // if (y > 0 && x > 0) // { // int indexF = y * cl.Cols + (x-1); // int indexT = (y-1) * cl.Cols + x; // GridCell from; // GridCell to; // from = GetFreeCellAt(item, Direction.left); // to = GetFreeCellAt(item, Direction.up); // if (from != null && to != null) // CalculatePath(from, to, indexF, indexT); // } // //Top to bottom // if (y > 0 && y < cl.Rows - 1) // { // int indexF = (y - 1) * cl.Cols + x; // int indexT = (y + 1) * cl.Cols + x; // GridCell from; // GridCell to; // from = GetFreeCellAt(item, Direction.up); // to = GetFreeCellAt(item, Direction.down); // if (from != null && to != null) // CalculatePath(from, to, indexF, indexT); // } // //Left To Right // if (x > 0 && x < cl.Cols - 1) // { // int indexF = y * cl.Cols + (x - 1); // int indexT = y * cl.Cols + (x + 1); // GridCell from; // GridCell to; // from = GetFreeCellAt(item, Direction.left); // to = GetFreeCellAt(item, Direction.rigth); // if (from != null && to != null) // CalculatePath(from, to,indexF,indexT); // } // index1++; //} } private void PredeterminePaths(Grid grid,int x1,int y1,int x2,int y2) { int indexF = Map.ClusterGrid.GetGridIndex(x1, y1); int indexT = Map.ClusterGrid.GetGridIndex(x2, y2); Grid gridA = Map.ClusterGrid.GetGrid(x1, y1); GridCell cellA= GetValidCellFromEntrance(gridA,grid); Grid gridB= Map.ClusterGrid.GetGrid(x2, y2); GridCell cellB = GetValidCellFromEntrance(gridB, grid); if (cellB != null && cellA != null) { CalculatePath( grid.GetCell(cellA.X,cellA.Y), grid.GetCell(cellB.X, cellB.Y), indexF, indexT); } } private GridCell GetValidCellFromEntrance(Grid fromGrid,Grid toGrid) { foreach (var entrance in Map.ClusterGrid.Entrances) { if (entrance.CellA.Parent == fromGrid && entrance.CellB.Parent==toGrid) return entrance.CellB; if (entrance.CellB.Parent == fromGrid && entrance.CellA.Parent == toGrid) return entrance.CellA; } return null; } private void SetVerticalEntrancesOnGrid(int x, int y) { bool checkEast = false; bool checkWest = false; Grid thisGrid = Map.ClusterGrid.GetGrid(x, y); if (x > 0) checkWest= true; if (x < Map.ClusterGrid.Cols- 1) checkEast = true; if (checkEast) { Grid eastGrid = Map.ClusterGrid.GetGrid(x+1, y); SetVertEntranceBetweenGrids(thisGrid, eastGrid); } if (checkWest) { Grid westGrid= Map.ClusterGrid.GetGrid(x-1, y); SetVertEntranceBetweenGrids(thisGrid, westGrid); } } private void SetVertEntranceBetweenGrids(Grid gridA, Grid gridB) { int yIndexa = 0; int yIndexb = gridA.Cols- 1; if (gridB.Position.X > gridA.Position.X) { yIndexa = yIndexb; yIndexb = 0; } //Start near the middle int startRow = gridA.Rows/ 2; //Currently supports 1 transition per set of grids bool continueChecking = true; //Loop to end ignoring the corner cell for (int i = startRow; i < gridA.Rows - 1; i++) { GridCell _edgeCell = gridA.GetCell(yIndexa,i); GridCell _adjacentCell = gridB.GetCell(yIndexb,i); if (_edgeCell.Labels[AIConsts.BLOCKED_TILE] == 0 && _adjacentCell.Labels[AIConsts.BLOCKED_TILE] == 0) { Map.ClusterGrid.AddEntrance(_edgeCell, _adjacentCell); continueChecking = false; break; } } if (continueChecking) { //Loop to beginning column ignoring the corner cell for (int i = startRow; i > 0; i--) { GridCell _edgeCell = gridA.GetCell(yIndexa, i); GridCell _adjacentCell = gridB.GetCell(yIndexb, i); if (_edgeCell.Labels[AIConsts.BLOCKED_TILE] == 0 && _adjacentCell.Labels[AIConsts.BLOCKED_TILE] == 0) { Map.ClusterGrid.AddEntrance(_edgeCell, _adjacentCell); continueChecking = false; break; } } } } private void SetHorizontalEntrancesOnGrid(int x, int y) { bool checkNorth = false; bool checkSouth = false; Grid thisGrid = Map.ClusterGrid.GetGrid(x,y); if (y > 0) checkNorth = true; if (y < Map.ClusterGrid.Rows-1) checkSouth= true; if (checkNorth) { Grid northGrid = Map.ClusterGrid.GetGrid(x, y - 1); SetHorizEntranceBetweenGrids(thisGrid, northGrid); } if (checkSouth) { Grid southGrid = Map.ClusterGrid.GetGrid(x, y + 1); SetHorizEntranceBetweenGrids(thisGrid, southGrid); } } private void SetHorizEntranceBetweenGrids(Grid gridA, Grid gridB) { int yIndexa = 0; int yIndexb = gridA.Rows - 1; if (gridB.Position.Y > gridA.Position.Y) { yIndexa = yIndexb; yIndexb = 0; } //Start near the middle int startCol = gridA.Cols / 2; //Currently supports 1 transition per set of grids bool continueChecking = true; //Loop to end ignoring the corner cell for (int i = startCol; i < gridA.Cols - 1; i++) { GridCell _edgeCell = gridA.GetCell(i, yIndexa); GridCell _adjacentCell = gridB.GetCell(i, yIndexb); if (_edgeCell.Labels[AIConsts.BLOCKED_TILE] == 0 && _adjacentCell.Labels[AIConsts.BLOCKED_TILE] == 0) { Map.ClusterGrid.AddEntrance(_edgeCell, _adjacentCell); continueChecking = false; break; } } if (continueChecking) { //Loop to beginning column ignoring the corner cell for (int i = startCol; i > 0; i--) { GridCell _edgeCell = gridA.GetCell(i, yIndexa); GridCell _adjacentCell = gridB.GetCell(i, yIndexb); if (_edgeCell.Labels[AIConsts.BLOCKED_TILE] == 0 && _adjacentCell.Labels[AIConsts.BLOCKED_TILE] == 0) { Map.ClusterGrid.AddEntrance(_edgeCell, _adjacentCell); continueChecking = false; break; } } } } private void CalculatePath(GridCell from, GridCell to,int gridfrom,int gridto) { PathFinding.PathFinder _f = new Brains.Framework.PathFinding.PathFinder(); _f.Grid = from.Parent; _f.StartNode = from; _f.EndNode = to; _f.Reset(); while (_f.State == Brains.Framework.PathFinding.PathFinderState.Working) { _f.Iterate(); } if (_f.State == Brains.Framework.PathFinding.PathFinderState.Finished) { // copying nodes PreStoredPath _path = new PreStoredPath(); _path.Nodes = new List(); int counter = 0; //for (int index = pathFinder.closeList.Count - 1; index >= 0; index--) for (int index = 0; index <= _f.closeList.Count - 1; index++) { StoredPathNode newNode = new StoredPathNode(); Grid grid = from.Parent; GridCell _node = grid.GetCell(_f.closeList[index].X, _f.closeList[index].Y); newNode.nodePosition.X = grid.GetCell(_f.closeList[index].X, _f.closeList[index].Y).Position.X; newNode.nodePosition.Y = grid.GetCell(_f.closeList[index].X, _f.closeList[index].Y).Position.Y; newNode.X = _f.closeList[index].X; newNode.Y = _f.closeList[index].Y; _path.FromGrid =gridfrom; _path.ToGrid = gridto; //pathNodes[counter] = newNode; _path.Nodes.Add(newNode); counter++; } from.Parent.PredeterminedPaths.Add(_path); } if (_f.State == Brains.Framework.PathFinding.PathFinderState.Failed) { Console.WriteLine(""); } } /// /// Adds the specified Actor to the Actors collection /// /// The Actor to add public void AddActor(Agent actor) { actor.ParentWorld = this; Actors.Add(actor); _spatialtable.RegisterObject(actor); } /// /// Loads Map data from a texture /// /// The texture to use /// The size of a cells width or height /// The type of grid to create public void LoadMapDataFromTexture(Texture2D texture, int cellSize,Type grid) { LoadMapDataFromTexture(texture, 1, 1, cellSize, grid); } /// /// Loads Map data from a texture /// /// The texture to use /// The size of a cells width or height public void LoadMapDataFromTexture(Texture2D texture, int cellSize) { LoadMapDataFromTexture(texture, 1, 1, cellSize, typeof(Grid)); } /// /// Loads Map data from a texture /// /// The texture to use /// The size of a cells width or height /// The number of cols to split the map into /// The number of rows to split the map into /// The type of grid to create public void LoadMapDataFromTexture(Texture2D texture, int clusterColumnSplit, int clusterRowSplit, int cellSize, Type gridType) { _mapTexture = texture; Vector2 position = new Vector2(); int clusterCols = texture.Width / clusterColumnSplit; int clusterRows = texture.Height / clusterRowSplit; SetupMap( texture.Width * cellSize, texture.Height * cellSize, cellSize, clusterCols, clusterRows, gridType); int index = 0; for (int y = 0; y < clusterRowSplit; y++) { for (int x = 0; x < clusterColumnSplit; x++) { Grid item = Map.ClusterGrid.Grids[index]; index++; int count = item.Rows * item.Cols; Color[] colorData = new Color[count]; texture.GetData( 0, new Rectangle(x * clusterCols, y * clusterRows, clusterCols, clusterRows), colorData, 0, count); item.ColorData = colorData; int _index = 0; for (int yy = 0; yy < item.Rows; yy++) { for (int xx = 0; xx < item.Cols; xx++) { GridCell cell = item.GetCell(xx, yy); cell.Labels[AIConsts.COLORR] = colorData[_index].R; cell.Labels[AIConsts.COLORG] = colorData[_index].G; cell.Labels[AIConsts.COLORB] = colorData[_index].B; cell.Labels[AIConsts.COLORA] = colorData[_index].A; if (colorData[_index] == Color.Black) { cell.Type = 0; } //HACK FOR WATER if (colorData[_index] == new Color(0,17,255)) { cell.Type = 2; } _index++; } } } } AnotateMap(); } /// /// Adds an Actor to the Actors collection at the specified position and size /// /// The position to start the Actor at /// The radius of the Actor /// public Agent AddActor(Vector2 position, float radius) { Agent _actor = new Agent(position, radius); AddActor(_actor); return _actor; } /// /// Removes an Agent from the world /// /// public void RemoveAgent(Agent agent) { _agentsToRemove.Add(agent); } public void AddGroup(Group group) { Groups.Add(group.GroupID, group); } public Group GetGroup(int id) { return Groups[id]; } } }