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];
}
}
}