using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Brains.Framework.Locomotion;
using Brains.Framework.Behaviors;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using Brains.Framework.Designer;
using Brains.Framework.Map;
namespace Brains.Framework
{
///
/// Represents an autonomous agent in the AI world.
///
/// You can implement your own Agent by inheriting from this class
public class Agent
{
private static int _id=0;
private float _rotation = 0;
private IBehavior _rootBehavior;
private LocomotionController _motionController;
///
/// Gets or sets the Feelers on the agent.
///
public List Feelers { get; set; }
///
/// Gets or sets the name of the Agent
///
public string Name { get; set; }
private int NextID
{
get
{
return _id++;
}
}
public Dictionary Labels;
///
/// Gets the cells the agent is in
///
/// Current implementation only supports 1
public List CellsIAmIn { get; set; }
///
/// The position of the Actor
///
public Vector2 Position { get; set; }
///
/// Gets or sets the previous position of the agent
///
public Vector2 PreviousPosition{ get; set; }
///
/// The position the Actor would like to be at
///
public Vector2 DesiredPosition { get; set; }
///
/// The Actors Radius
///
public float Radius { get; set; }
///
/// The Orientation of the Actor
///
/// This value is normalized
public Vector2 Orientation { get; set; }
///
/// The Orientation the Actor would like to be facing
///
public Vector2 DesiredOrientation { get; set; }
///
/// The world in which this Actor is alive
///
public World ParentWorld { get; set; }
///
/// The rotation of the Actor
///
public float Rotation { get { return _rotation; } }
///
/// The Root Behavior of the Actor
///
public IBehavior RootBehavior
{
get { return _rootBehavior; }
set
{
_rootBehavior = value;
_rootBehavior.SetOwner(this);
}
}
///
/// Gets or sets the Locomotion object for the Agent
///
/// You can provide your own locomotion technique by inheriting the Locomotion class
public LocomotionController Locomotion
{
get { return _motionController; }
set
{
_motionController = value;
if (_motionController != null)
_motionController.Owner = this;
}
}
///
/// Default constructor of the agent
///
/// Center position of the Agent
/// The radius of the agent
public Agent(Vector2 position, float radius)
{
Position = DesiredOrientation = position;
Radius = radius;
Orientation = DesiredOrientation = new Vector2(1, 0);
Feelers = new List();
CellsIAmIn = new List();
AddFeeler(1, 0,Radius*4);
AddFeeler(.5f, .5f, Radius + 32);
AddFeeler(.5f, -.5f, Radius + 32);
Name = CreateUniqueName();
Labels = new Dictionary();
}
private string CreateUniqueName()
{
return "Agent #" + NextID;
}
private void AddFeeler(float x, float y,float length)
{
Feelers.Add(new Feeler(new Vector2(x, y),length,this));
}
public virtual void UpdatePerception(GameTime gameTime)
{
//Tag nearby
}
///
/// Update the agent every frame.
///
/// The time elapsed since the last update
public virtual void Update(GameTime gameTime)
{
if (RootBehavior != null &&
(RootBehavior.State == BehaviorState.Idle || RootBehavior.State == BehaviorState.Running))
{
RootBehavior.Update(gameTime);
}
PreviousPosition = Position;
if (Locomotion != null)
Locomotion.Update(gameTime);
if (PreviousPosition != Position || CellsIAmIn.Count == 0)
UpdateLocationData();
}
///
/// Every time an actor changes its position, it remembers all map's nodes
/// he is in.
///
private void UpdateLocationData()
{
// 1. remove itself from all previous AINodes
for (int index = 0; index < CellsIAmIn.Count; index++)
{
// remove myself from AINode I was registered previously
CellsIAmIn[index].Agents.Remove(this);
}
CellsIAmIn.Clear();
// 2. add itself to all
// - get distance from the center of a map,
// - extract x and y components,
// - for each component calculate row and col
Vector2 vDistanceFromMapCenter = this.Position;
Grid map = ParentWorld.Map.ClusterGrid.GetGridAtPosition(Position);
if (map == null)
return;
float _hstep = (map.Width/ ((float)map.Cols));
float _vstep = (map.Height/ ((float)map.Rows));
float xComponent = (vDistanceFromMapCenter.X / _hstep) ;
float yComponent = (vDistanceFromMapCenter.Y / _vstep);
// xComponent = xComponent - 0.5f;
//yComponent = yComponent - 0.5f;
//debugX = (int)xComponent;
//debugY = (int)yComponent;
int indexX = (int)xComponent;
int indexY = (int)yComponent;
indexX = indexX % map.Cols;
indexY = indexY % map.Rows;
map = ParentWorld.Map.ClusterGrid.GetGridAtPosition(Position);
if (map != null)
{
GridCell desiredNode = map.GetCell(indexX, indexY);
if (desiredNode != null)
{
// am I out of map?
desiredNode.Agents.Add(this);
CellsIAmIn.Add(desiredNode);
}
else
{
Console.Write("");
}
}
else
{
Console.Write("");
}
}
///
/// Loads a Behavior from a file
///
/// The filename to load
protected void LoadBehaviorFromFile(string filename)
{
StreamReader _reader = new StreamReader(filename);
XmlSerializer _ser = new XmlSerializer(typeof(BehaviorNode));
BehaviorNode _rootNode = (BehaviorNode)_ser.Deserialize(_reader);
_reader.Close();
CompositeBehavior _behavior = CreateBehaviorInstance(_rootNode.Parameters[0].Value);
SetupBehavior(_behavior, _rootNode);
RootBehavior = _behavior;
}
private void SetupBehavior(IBehavior parentBehavior, BehaviorNode rootNode)
{
foreach (var item in rootNode.SubBehaviors)
{
IBehavior behavior = CreateBehaviorInstance(item.TypeName);
foreach (var param in item.Parameters)
{
if (param.Name == "RootType")
continue;
PropertyInfo prop = behavior.GetType().GetProperty(param.Name);
if(prop.PropertyType ==typeof(bool))
prop.SetValue(behavior, bool.Parse(param.Value), null);
else if (prop.PropertyType == typeof(float))
prop.SetValue(behavior, float.Parse(param.Value), null);
else
prop.SetValue(behavior, param.Value, null);
}
if(parentBehavior is ISubBehaviorHolder)
((ISubBehaviorHolder)parentBehavior).SubBehaviors.Add(behavior);
SetupBehavior(behavior, item);
}
}
private CompositeBehavior CreateBehaviorInstance(string typeName)
{
foreach (var item in ParentWorld.Engine.Assemblies)
{
CompositeBehavior behavior =(CompositeBehavior)item.CreateInstance(typeName);
if (behavior != null)
return behavior;
}
return null;
}
///
/// Gets the cost of a grid cell type for use with pathfinding.
///
/// The Type value from a GridCell
/// The cost of the cell
/// Override this method for greater flexibility in pathfinding
public virtual byte GetCostOfCellType(int type)
{
if (type == 0)
return 99;
else
return (byte)type;
}
public void SetLabel(uint labelid, int labelvalue)
{
if (!Labels.Keys.Contains(labelid))
Labels.Add(labelid, labelvalue);
else
Labels[labelid] = labelvalue;
}
public int GetLabel(uint labelid)
{
if (!Labels.Keys.Contains(labelid))
return 0;
else
return Labels[labelid];
}
public List GetNearbyAgents(int distance)
{
return ParentWorld._spatialtable.GetNearbyItems(this, distance);
}
}
}