root/src/BRAINSFramework/Agent.cs

User picture

Author: conkerjo

Revision: 30 («Previous)


File Size: 11 KB

(July 18, 2009 09:44 UTC) Almost 3 years ago

Missing Files

 
Show/hide line numbers
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
{
    /// <summary>
    /// Represents an autonomous agent in the AI world.
    /// </summary>
    /// <remarks>You can implement your own Agent by inheriting from this class</remarks>
    public class Agent
    {
        private static int _id=0;

        private float _rotation = 0;
        private IBehavior _rootBehavior;
        private LocomotionController _motionController;

        /// <summary>
        /// Gets or sets the Feelers on the agent.
        /// </summary>
        public List<Feeler> Feelers { get; set; }

        /// <summary>
        /// Gets or sets the name of the Agent
        /// </summary>
        public string Name { get; set; }

        private int NextID
        {
            get
            {
                return _id++;
            }
        }

        public Dictionary<uint, int> Labels;

        /// <summary>
        /// Gets the cells the agent is in
        /// </summary>
        /// <remarks>Current implementation only supports 1</remarks>
        public List<GridCell> CellsIAmIn { get; set; }

        /// <summary>
        /// The position of the Actor
        /// </summary>
        public Vector2 Position { get; set; }
        
        /// <summary>
        /// Gets or sets the previous position of the agent
        /// </summary>
        public Vector2 PreviousPosition{ get; set; }

        /// <summary>
        /// The position the Actor would like to be at
        /// </summary>
        public Vector2 DesiredPosition { get; set; }

        /// <summary>
        /// The Actors Radius
        /// </summary>
        public float Radius { get; set; }

        /// <summary>
        /// The Orientation of the Actor
        /// </summary>
        /// <remarks>This value is normalized</remarks>
        public Vector2 Orientation { get; set; }

        /// <summary>
        /// The Orientation the Actor would like to be facing
        /// </summary>
        public Vector2 DesiredOrientation { get; set; }

        /// <summary>
        /// The world in which this Actor is alive
        /// </summary>
        public World ParentWorld { get; set; }

        /// <summary>
        /// The rotation of the Actor
        /// </summary>
        public float Rotation { get { return _rotation; } }

        /// <summary>
        /// The Root Behavior of the Actor
        /// </summary>
        public IBehavior RootBehavior
        {
            get { return _rootBehavior; }
            set
            {
                _rootBehavior = value;
                _rootBehavior.SetOwner(this);
            }
        }

        /// <summary>
        /// Gets or sets the Locomotion object for the Agent
        /// </summary>
        /// <remarks>You can provide your own locomotion technique by inheriting the Locomotion class</remarks>
        public LocomotionController Locomotion
        {
            get { return _motionController; }
            set
            {
                _motionController = value;
                if (_motionController != null)
                    _motionController.Owner = this;
            }
        }

        /// <summary>
        /// Default constructor of the agent
        /// </summary>
        /// <param name="position">Center position of the Agent</param>
        /// <param name="radius">The radius of the agent</param>
        public Agent(Vector2 position, float radius)
        {
            Position = DesiredOrientation = position;
            Radius = radius;
            Orientation = DesiredOrientation = new Vector2(1, 0);
            Feelers = new List<Feeler>();
            CellsIAmIn = new List<GridCell>();
            AddFeeler(1, 0,Radius*4);
            AddFeeler(.5f, .5f, Radius + 32);
            AddFeeler(.5f, -.5f, Radius + 32);

            Name = CreateUniqueName();
            Labels = new Dictionary<uint, int>();
        }

        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
        }

        /// <summary>
        /// Update the agent every frame. 
        /// </summary>
        /// <param name="gameTime">The time elapsed since the last update</param>
        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();
        }

        /// <summary>
        /// Every time an actor changes its position, it remembers all map's nodes
        /// he is in.
        /// </summary>
        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("");
            }
        }
        
        /// <summary>
        /// Loads a Behavior from a file
        /// </summary>
        /// <param name="filename">The filename to load</param>
        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;
        }

        /// <summary>
        /// Gets the cost of a grid cell type for use with pathfinding.
        /// </summary>
        /// <param name="type">The Type value from a GridCell</param>
        /// <returns>The cost of the cell</returns>
        /// <remarks>Override this method for greater flexibility in pathfinding</remarks>
        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<Agent> GetNearbyAgents(int distance)
        {
            return ParentWorld._spatialtable.GetNearbyItems(this, distance);
        }
    }
}