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