//---------------------------------------------------------------------------------------------------------------------------------------------------
// <copyright file="Statics.cs" company="DarkWynter Studios">
//     Copyright (C)2007 DarkWynter Studios.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {Contact : darkwynter.com for licensing information
//---------------------------------------------------------------------------------------------------------------------------------------------------

#define forPC

namespace DarkWynterEngine.ObjectLib
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using System.Xml;
    using System.Diagnostics;
    using System.Threading;
    #endregion

    using Audio;
    using Physics;
    using Globals;
    using GameObjects;
    using PhysicsGpu;
    using Draw;

    /// <summary>
    /// Container and Manager class for GameObjects
    /// </summary>
    public class ObjectLibrary
    {
        #region Attributes

        /// <summary>
        /// Contains all the Type definitions for GameObjects
        /// </summary>
        public List<GameObjectTypes> gameObjectTypes;

        // STANDARD OBJECTS =================================================
        /// <summary>
        /// Terrain object.
        /// </summary>
        public Terrain terrain;
        /// <summary>
        /// A list of all Human Players
        /// </summary>
        public List<Human> humans;
        /// <summary>
        /// A list of opponent AI Players
        /// </summary>
        public List<AI> bots;

        // GENERIC OBJECTS ================================================= 
        /// <summary>
        /// Billboards requested by GameObjects
        /// </summary>
        private List<BillboardList> billboards;
        /// <summary>
        /// User Supplied GameObjects
        /// </summary>
        private List<GameObject> gameObjectList;
        /// <summary>
        /// Cpu-based Props Loaded from XML
        /// </summary>
        private List<PropList> propLists;
        /// <summary>
        /// Gpu-based Props Loaded from XML
        /// </summary>
        public List<GpuObjectList> gpuObjectLists;

       

        /// <summary>
        ///  Keeps Model Information for Lists
        /// </summary>
        public ModelManager modelManager;

        /// <summary>
        /// Drawn on HUD to identify AI at a distance.
        /// </summary>
        private Texture2D enemyTargetReticle;

        #endregion

        #region Methods
        /// <summary>
        /// CONSTRUCTOR:
        /// </summary>
        public ObjectLibrary(List<GameObjectTypes> userDefinedTypes)
        {             
            Statics.LevelSettings.darkMatterIterator = 0;

            if (Statics.LevelSettings.darkMatter == null)
            {
                Statics.LevelSettings.darkMatter = new List<Mass>(Statics.LevelSettings.darkMatterCount);

                for (int i = 0; i < Statics.LevelSettings.darkMatterCount; i++)
                {
                    Statics.LevelSettings.darkMatter.Add(new Mass());
                }
            }
            else
            {
                for (int i = 0; i < Statics.LevelSettings.darkMatterCount; i++)
                {
                    Statics.LevelSettings.darkMatter[i].ClearValues();
                }

                Statics.GameSettings.accelDueToGravity = Vector3.Zero;
                Statics.GameSettings.gravityForce = 0.0f;
            }

            // Load Engine and Game supplied GameObjectTypes
            gameObjectTypes = new List<GameObjectTypes>();
            gameObjectTypes.Add(new GameObjectTypes());
            gameObjectTypes.AddRange(userDefinedTypes);

            Statics.SystemSettings.TOTAL_PLAYERS = 0;
            humans = new List<Human>();
            for (int playerNumber = 0; playerNumber < 4; playerNumber++)
            {
                humans.Add(new Human());
                humans[playerNumber].playerIndex = playerNumber;
            }

        }

        /// <summary>
        /// Load the Terrain and Skysphere for the Terrain Demo
        /// </summary>
        public void LoadLevel()
        {
            // Load Level XML
            XML.LoadLevel(XML.levelInfo[0].filepath);

            // Create the model manager 
            modelManager = new ModelManager();

            // Terrain
            if (XML.gameObjectInfo.terrainNode != null)
            {
                terrain = new Terrain();
                terrain.Load(XML.gameObjectInfo.terrainNode, this);
            }


            // Load Audio
            if (XML.gameObjectInfo.musicNode != null)
            {
                Audio.LoadXML(XML.gameObjectInfo.musicNode);
            }



            // Game Objects - Single Depth List
            gameObjectList = new List<GameObject>();
            if (XML.gameObjectInfo.gameObjectNodes != null)
            {
                foreach (XmlNode objectNode in XML.gameObjectInfo.gameObjectNodes.ChildNodes)
                {
                    GameObject gameObject = new GameObject();
                    if (!CreateGameObjectType(objectNode, ref gameObject))
                    {
                        // GameObject could not be created
                        continue;
                    }
                    if(gameObject.Load(objectNode, this))
                    {
                        gameObjectList.Add(gameObject);
                    }
                }
            }

            // Props - List of Lists
            propLists = new List<PropList>();
            if (XML.gameObjectInfo.propNode != null)
            {
                foreach (XmlNode objectNode in XML.gameObjectInfo.propNode.ChildNodes)
                {
                    PropList propList = new PropList();
                    if (propList.Load(objectNode, this))
                    {
                        propLists.Add(propList);
                    }
                }
            }

            // GpuObjects - List of Lists
            gpuObjectLists = new List<GpuObjectList>();
            if (XML.gameObjectInfo.gpuObjectNodes != null)
            {
                foreach (XmlNode objectNode in XML.gameObjectInfo.gpuObjectNodes.ChildNodes)
                {
                    GpuObjectList gpuObjectList = new GpuObjectList();
                    gpuObjectList.Load(objectNode, this);
                    gpuObjectLists.Add(gpuObjectList);
                }
            }

            // Props Billboards - ( Dynamic GameObject Billboards handled in Draw_Billboards)
            billboards = new List<BillboardList>();
            if (XML.gameObjectInfo.billboardNode != null)
            {
                foreach (XmlNode objectNode in XML.gameObjectInfo.billboardNode.ChildNodes)
                {
                    BillboardList billboardList = new BillboardList();
                    billboardList.Load(objectNode, this);
                    billboards.Add(billboardList);
                }
            }

            // Humans
            humans = new List<Human>();
            if (XML.gameObjectInfo.humanNodes != null)
            {
                foreach (XmlNode objectNode in XML.gameObjectInfo.humanNodes.ChildNodes)
                {
                    GameObject gameObject = new Human();
                    if (gameObject.Load(objectNode, this))
                    {
                        humans.Add((Human)gameObject);
                    }
                }
            }

            // Bots
            bots = new List<AI>();
            if (XML.gameObjectInfo.botNodes != null)
            {
                int botCounter = 0;
                foreach (XmlNode objectNode in XML.gameObjectInfo.botNodes.ChildNodes)
                {
                    if (botCounter < Statics.GameSettings.botCount)
                    {
                        GameObject gameObject = new AI();
                        if (gameObject.Load(objectNode, this))
                        {
                            bots.Add((AI)gameObject);
                        }
                        botCounter++;
                    }
                }
            }

            enemyTargetReticle = Statics.SystemSettings.content.Load<Texture2D>("Content/_textures/TargetMarker");

        }

        /// <summary>
        /// Create an GameObject using the "type" string included in the XmlNode.
        /// User-Defined types can be added to the engine by inheriting from GameObjectTypes
        ///   and overloading it's CreateGameobjectType method.  This method must create a new
        ///   GameObject and assign it to the incoming GameObject.  Creation of the GameObject
        ///   by the overloaded User-Defined class should use a case statement to decide what 
        ///   kind of GameObject to create based on the Xml-based "type" parameter.
        /// </summary>
        /// <param name="objectNode">Xml data describing the Gameobject.</param>
        /// <param name="gameObject">A blank GameObject to replace with User-Defined GameObject.</param>
        /// <returns>Returns true if GameObject was successfully created.</returns>
        public bool CreateGameObjectType(XmlNode objectNode, ref GameObject gameObject)
        {
            // Get the Object Type
            string type = objectNode.LocalName;
            for (int i = 0; i < gameObjectTypes.Count; i++)
            {
                // Try and create the gameObject
                bool found = gameObjectTypes[i].CreateGameObjectType(type, ref gameObject);
                if (found) { return true; }
            }
            return false;
        }


        /// <summary>
        /// Call all GameObject Updates to handle gravity and motion
        /// </summary>
        /// <param name="objectLibrary">ObjectLibrary</param>
        public void Update(ref ObjectLibrary objectLibrary, Collision collision)
        {
            // TEMP: Remove when LookupMap reinstalled
            List<Mass> collisionList = new List<Mass>();
            for (int i = 0; i < gameObjectList.Count; i++)
            {
                collisionList.Add(gameObjectList[i].mass);
            }
            for (int i = 0; i < humans.Count; i++)
            {
                collisionList.Add(humans[i].mass);
            }
            for (int i = 0; i < bots.Count; i++)
            {
                collisionList.Add(bots[i].mass);
            }

            // END TEMP ============================


            collision.UpdateCollision(ref objectLibrary, collisionList);

            // Modify terrain if changed
            terrain.Update(ref objectLibrary);


            // Update Dynamic Props, Particles, and Quads
            //Statics.lookupMap.UpdateDynamic(this);

            for (int i = 0; i < humans.Count; i++)
            {
                humans[i].Update(ref objectLibrary);
            }

            for (int i = 0; i < bots.Count; i++)
            {
                bots[i].Update(ref objectLibrary);
            }

            for (int i = 0; i < gameObjectList.Count; i++)
            {
                gameObjectList[i].Update(ref objectLibrary);
            }

            // Update Cpu Based Object Lists
            for (int i = 0; i < propLists.Count; i++)
            {
                propLists[i].Update(ref objectLibrary);
            }

            // Update Gpu Based Object Lists
            for (int i = 0; i < gpuObjectLists.Count; i++)
            {
                gpuObjectLists[i].Update(this);
            }
        }
        /// <summary>
        /// Remove a Game Object from our list of gameobjects
        /// </summary>
        /// <param name="gameObject">Game object to be removed</param>
        public void RemoveGameObject(GameObject gameObject)
        {
            gameObjectList.Remove(gameObject);
        }
        /// <summary>
        /// Player uses Attack Method to shoot Grenade-like objects onto the board
        /// </summary>
        /// <param name="playerID">ID of the player who fired the grenade</param>
        /// <param name="type">Type of grenade</param>
        /// <param name="initialPosition">Starting position of the grenade</param>
        /// <param name="direction">Normal direction of the grenade</param>
        /// <param name="particleMassValue">Mass of grenade</param>
        /// <param name="particleThermalValue">Thermal value of grenade</param>
        public void Attack_Grenade(int playerID, 
                                   Enums.ParticleType type, 
                                   Vector3 initialPosition, 
                                   Vector3 direction, 
                                   int particleMassValue, 
                                   int particleThermalValue)
        {
            XmlNode particlesNode = XML.gameObjectInfo.particleNode;

            // Set it's properties
            Particle particle = new Particle();
            particle.Load(particlesNode, this);
            particle.PrepAttack(playerID, type, initialPosition, direction, particleMassValue, particleThermalValue);
            gameObjectList.Add(particle);
        }
        /// <summary>
        /// Player uses Attack Method to shoot Bullet-like objects onto the board
        /// </summary>
        /// <param name="playerID">ID of the player who fired the Bullet</param>
        /// <param name="type">Type of Bullet</param>
        /// <param name="initialPosition">Starting position of the Bullet</param>
        /// <param name="direction">Normal direction of the Bullet</param>
        /// <param name="bulletMassValue">Mass of Bullet</param>
        /// <param name="bulletThermalValue">Thermal value of Bullet</param>
        public void Attack_Bullet(int playerID, 
                                  Enums.BulletType type, 
                                  Vector3 initialPosition, 
                                  Vector3 direction, 
                                  int bulletMassValue, 
                                  int bulletThermalValue)
        {
            XmlNode bulletNode = XML.gameObjectInfo.bulletNode;

            // Set it's properties
            Bullet bullet = new Bullet();
            bullet.Load(bulletNode, this);
            bullet.PrepAttack(playerID, type, initialPosition, direction, bulletMassValue, bulletThermalValue);
            gameObjectList.Add(bullet);
        }
        /// <summary>
        /// Player uses Attack Method to shoot Swarm-like objects onto the board
        /// </summary>
        /// <param name="playerID">ID of the player who fired the Swarm</param>
        /// <param name="initialPosition">Starting position of the Swarm</param>
        /// <param name="direction">Normal direction of the Swarm</param>
        /// <param name="swarmMassValue">Mass of Swarm</param>
        /// <param name="swarmThermalValue">Thermal value of Swarm</param>
        public void Attack_Swarm(int playerID, Vector3 initialPosition, Vector3 direction, int swarmMassValue, int swarmThermalValue)
        {
            XmlNode objectNode = XML.gameObjectInfo.gpuObjectNodes;
            objectNode = objectNode.FirstChild;

            // Create Object based on type matching in GameObjectTypes
            GameObject gameObject = new GameObject();
            if (CreateGameObjectType(objectNode, ref gameObject))
            {
                // Set up Model Manager
                string type = objectNode.Attributes["type"].Value;
                modelManager.AddNewModel(type,
                               objectNode.Attributes["model"].Value,
                               objectNode.Attributes["texture"].Value,
                               objectNode.Attributes["bumpTexture"].Value);

                // Start Type properties
                gameObject.mass.gameObjectPointer = gameObject;
                gameObject.objectModelName = type;

                // Set Position
                gameObject.mass.SetPosition(initialPosition + (Statics.PlayerSettings.PARTICLE_START_DISTANCE * direction), 
                                            initialPosition + ((Statics.PlayerSettings.PARTICLE_START_DISTANCE - 10) * direction));
                //gameObject.mass.lastPosition = Vector3.Zero + initialPosition;
                gameObject.mass.boundingVolume = new BoundingVolume(new BoundingSphere(gameObject.mass.currentPosition, 2.0f));

                
                // Set Direction
                //direction.Normalize();
                //gameObject.mass.normalVector = Vector3.Zero + direction;


                gameObject.mass.velocity = Vector3.Zero;
                gameObject.mass.acceleration = Vector3.Zero;
                gameObject.mass.scale = new Vector3((int)(float.Parse(objectNode.Attributes["maxScale"].Value)));
                gameObject.mass.mass = float.Parse(objectNode.Attributes["mass"].Value);

                // Add Force
                gameObject.mass.AddForce(direction * 10000.0f);

                gameObject.objectValues = new VertexFogBinormalTangent();
                gameObject.objectValues.Fog = new Vector4(gameObject.mass.scale, 0.0f);                // Store the scale
                gameObject.objectValues.Binormal = new Vector4(gameObject.mass.currentRotation.X,
                                                               gameObject.mass.currentRotation.Y,
                                                               gameObject.mass.currentRotation.Z,
                                                               gameObject.mass.currentRotation.W);       // Store the quaternion rotation
            }

            // INSERT GPUOBJECTLIST ADD HERE.....
            gpuObjectLists[0].AddMass(gameObject.mass);
        }


        /// <summary>
        /// Draw and Postdrawing
        /// </summary>
        /// <param name="playerIndex">Player for which draw perspective is associated.</param>
        public void Draw(int playerIndex)
        {
            // Send Lighting to the Shader
            ShaderParameters.lightPosition0.SetValue(Statics.LevelSettings.lightPosition1);
            ShaderParameters.lightAmbient0.SetValue(Statics.LevelSettings.lightAmbient1);
            ShaderParameters.lightDiffuse0.SetValue(Statics.LevelSettings.lightDiffuse1);
            ShaderParameters.lightSpecular0.SetValue(Statics.LevelSettings.lightSpecular1);
            if (Statics.SystemSettings.singleLightSource)
            {
                ShaderParameters.lightPosition1.SetValue(Statics.LevelSettings.lightPosition1);
                ShaderParameters.lightAmbient1.SetValue(Statics.LevelSettings.lightAmbient1);
                ShaderParameters.lightDiffuse1.SetValue(Statics.LevelSettings.lightDiffuse1);
                ShaderParameters.lightSpecular1.SetValue(Statics.LevelSettings.lightSpecular1);
            }
            else
            {
                ShaderParameters.lightPosition1.SetValue(Statics.LevelSettings.lightPosition2);
                ShaderParameters.lightAmbient1.SetValue(Statics.LevelSettings.lightAmbient2);
                ShaderParameters.lightDiffuse1.SetValue(Statics.LevelSettings.lightDiffuse2);
                ShaderParameters.lightSpecular1.SetValue(Statics.LevelSettings.lightSpecular2);
            }

            ShaderParameters.fogEnd.SetValue(Statics.LevelSettings.fogEnd);
            ShaderParameters.fogStart.SetValue(Statics.LevelSettings.fogStart);

            // TerrainLOD and TerrainMod Shader Requisits
            Statics.PlayerSettings.playerPosition      = humans[playerIndex].mass.currentPosition;
            Statics.PlayerSettings.playerLookVector = new Vector2(humans[playerIndex].mass.normalVector.X, humans[playerIndex].mass.normalVector.Z);
            Statics.PlayerSettings.playerRotation = humans[playerIndex].mass.currentRotation;
            Statics.TerrainSettings.terrainLookAtPoint  = humans[playerIndex].lookAtPoint;
            Statics.TerrainSettings.terrainModRange     = (int)(humans[playerIndex].attackMagnitude / 25);
            Statics.TerrainSettings.terrainModEnabled   = humans[playerIndex].terrainModEnabled;

            // Draw the terrain
            if (Statics.SystemSettings.enableTerrainBumpMapping)
            {
                terrain.Draw(modelManager, "TerrainShaderMain");
            }
            else
            {
                terrain.Draw(modelManager, "TerrainNoBumpsShaderMain");
            }

            // Draw User Defined Game Objects
            for (int i = 0; i < gameObjectList.Count; i++)
            {
                gameObjectList[i].Draw(modelManager,"BasicLightingShaderMain");
            }

            // Draw Cpu Based Object Lists
            for (int i = 0; i < propLists.Count; i++)
            {
                propLists[i].Draw(modelManager, "BasicLightingInstancedShaderMain");
            }

            // Draw Gpu Based Object Lists
            for (int i = 0; i < gpuObjectLists.Count; i++)
            {
                gpuObjectLists[i].Draw(modelManager, "GPUPhysixInstancedShaderMain");
            }

            
            //// Draw All Free Standing Billboards (Note: GameObject Billboards are drawn in DrawBillboards sequence)
            //for (int i = 0; i < billboards.Count; i++)
            //{            
            //    billboards[i].Draw_Billboards(this, playerIndex);
            //}

            
            // Draw AI 
            foreach (Player player in bots)
            {
                player.Draw(modelManager, "");
            }

            // Draw Humans
            foreach (Player human in humans)
            {
               // if (human != humans[playerIndex])
                //{
                    human.Draw(modelManager, "");
                //}

            }
            // Draw AI Shields (NOTE: draw 2nd to last for transparency reasons)
            foreach (Player player in bots)
            {
                if (player.IsAlive())
                {
                    player.DrawShield();
                }
            }
            // Draw Human Shields (NOTE: draw last for transparency reasons)
            foreach (Player player in humans)
            {
                if (player.IsAlive())
                {
                    player.DrawShield();
                }
            }
        }
        /// <summary>
        /// Pass a Billboard List to GameObjects so they can add GameObject-relative Billboards.
        /// This does not effect the Prop Billboards.
        /// </summary>
        /// <param name="playerIndex">Billboards are oriented towards the Player we are drawing them for.</param>
        public void Draw_Billboards(int playerIndex)
        {
            // Create Empty List
            BillboardList dynamicBillboards = new BillboardList();

            // Draw the terrain
            terrain.Draw_Billboards(this, dynamicBillboards);

            //// Draw Cpu Based Object Lists
            //for (int i = 0; i < propLists.Count; i++)
            //{
            //    propLists[i].Draw_Billboards(this, ref gameObjectBillboards);
            //}

            //// Draw Gpu Based Object Lists
            //for (int i = 0; i < gpuObjectLists.Count; i++)
            //{
            //    gpuObjectLists[i].Draw_Billboards(this, ref gameObjectBillboards);
            //}

            // Draw User Defined Game Objects
            for (int i = 0; i < gameObjectList.Count; i++)
            {
                gameObjectList[i].Draw_Billboards(this, dynamicBillboards);
            }

            // Draw Humans
            foreach (Player human in humans)
            {
                human.Draw_Billboards(this, dynamicBillboards);
            }

            // Draw AI 
            foreach (Player player in bots)
            {
                player.Draw_Billboards(this, dynamicBillboards);
            }

            // Draw all GameObject Billboards
            dynamicBillboards.Draw_Billboards(this, playerIndex);
        }

        /// <summary>
        /// Let each GameObject draw it's own Sprites
        /// </summary>
        /// <param name="currentPlayerIndex">Current player's index</param>
        /// <param name="spriteBatch">SpriteBatch used to draw</param>
        public void PostDraw_HUD(int currentPlayerIndex, SpriteBatch spriteBatch)
        {

            // Calculate the player's ModelView Matrix
            Statics.RenderSettings.matrixView = Matrix.CreateLookAt(humans[currentPlayerIndex].mass.currentPosition,
                                                     humans[currentPlayerIndex].mass.currentPosition + humans[currentPlayerIndex].mass.normalVector,
                                                     humans[currentPlayerIndex].mass.upVector);

            // Calculate the player's Projection Matrix
            Statics.RenderSettings.matrixProjection = Matrix.CreatePerspectiveFieldOfView((float)Math.PI / 4,
                                                                   Statics.RenderSettings.cameraList[currentPlayerIndex].viewport.Width / 
                                                                   Statics.RenderSettings.cameraList[currentPlayerIndex].viewport.Height,
                                                                   0.3f,
                                                                   100000f);
            // Post Draw for Humans
            if (humans[currentPlayerIndex].IsAlive())
            {
                //Draw the HUD for this human player
               humans[currentPlayerIndex].HUD.Draw(spriteBatch);
            }
            else
            {
                //Draw the dead HUD for dead player
                humans[currentPlayerIndex].deadHUD.Draw(spriteBatch);
            }

            // Mark Other Players
            //  -The further away the enemy is, the smaller and fainter the icon
            //  -scale range: 0.25-1.0, alpha range: 50-200
            float distance = 0.0f; float distRatio = 0.0f; float totalScale = 0.25f;
            Vector3 position = new Vector3();

            // Draw AI bots
            foreach (Player bot in bots)
            {
                if (bot.IsAlive())
                {
                    DrawEnemyTargets(currentPlayerIndex, spriteBatch, ref position, ref distance, ref distRatio, totalScale, bot);
                }
            }
            // Draw Humans
            foreach (Player player in humans)
            {
                if (player.IsAlive() && player != humans[currentPlayerIndex])
                {
                    DrawEnemyTargets(currentPlayerIndex, spriteBatch, ref position, ref distance, ref distRatio, totalScale, player);
                }
            }
            
        }

        private void DrawEnemyTargets(int currentPlayerIndex, SpriteBatch spriteBatch, ref Vector3 position, ref float distance, ref float distRatio, float totalScale, Player player)
        {
            distance = Vector3.Distance(player.mass.currentPosition, humans[currentPlayerIndex].mass.currentPosition);
            if (distance > Statics.PlayerSettings.ENEMY_MARKER_MIN_DISTANCE)
            {
                position = Statics.SystemSettings.graphics.GraphicsDevice.Viewport.Project(player.mass.currentPosition,
                                                                                  Statics.RenderSettings.matrixProjection,
                                                                                  Statics.RenderSettings.matrixView,
                                                                                  Matrix.Identity);

                if (position.Z < 1)
                {
                    position.X -= Statics.SystemSettings.graphics.GraphicsDevice.Viewport.X;
                    position.Y -= Statics.SystemSettings.graphics.GraphicsDevice.Viewport.Y;
                    if (position.X >= 0 && position.X < Statics.SystemSettings.graphics.GraphicsDevice.Viewport.Width &&
                        position.Y >= 0 && position.Y < Statics.SystemSettings.graphics.GraphicsDevice.Viewport.Height)
                    {
                        distRatio = 1.0f - (distance / Statics.PlayerSettings.ENEMY_MARKER_MAX_DISTANCE);
                        //if negative they are too far away
                        if (distRatio < 0)
                        {
                            distRatio = 0;
                        }
                        spriteBatch.Draw(enemyTargetReticle, new Rectangle((int)(position.X - (enemyTargetReticle.Width * distRatio * totalScale) / 2),
                                                                           (int)(position.Y - (enemyTargetReticle.Height * distRatio * totalScale)),
                                                                           (int)(enemyTargetReticle.Width * distRatio * totalScale),
                                                                           (int)(enemyTargetReticle.Height * distRatio * totalScale)),
                                                                           new Color(255, 255, 255, (byte)(200 * distRatio)));
                    }
                }
            }
        }
        #endregion
    }
}

/* Legacy Code

/// <summary>
/// Function to reset our list of human players
/// </summary>
public void DeinitializeHumans()
{
    //be warned.. MUST call Statics.CheckForControllers after this!!
    foreach (Human human in humans)
    {
        human.playerController.playerControllerType = Enums.ControllerType.NONE;
        human.health = 0;
        human.manna = 0;
    }
}
public void GetDrawObjects()
{
    for (int i = 0; i < humans.Count; i++)
    {
        if (humans[i].IsAlive())
        {
            lookupMap.GetDrawObjects(humans[i].mass, drawList[i]);
        }
    }
}

// Test Function to update energy distribution
public void UpdateHeatDissipation(ObjectLibrary objectLibrary, float dt)
{
    List<Particle> newParticlesToAddToList = new List<Particle>();

    foreach (Particle particle in particles)
    {
        particle.mass.UpdatePosition(dt);
    }

    // Iterate through dynamic particles
    for (int i = 0; i < particles.Count; i++)
    {
        Particle particle = particles[i];

        // List to store the pointers to neighboring particles
        List<Particle> neighboringParticleList = new List<Particle>();

        // Used to calculate the average value of all neighboring particles
        float averageEnergy = particle.mass.energy;

        // Get the list of neighboring particle pointers
        neighboringParticleList = GetSurroundingParticles(particle.mass.currentPosition, objectLibrary);

        // Calculate their total energy
        foreach (Particle neigborParticle in neighboringParticleList)
        {
            averageEnergy += neigborParticle.mass.energy;
        }

        // Find the average
        averageEnergy /= neighboringParticleList.Count + 1;

        // If the average value is not equal to zero then we have some heat dissipation to do
        if (averageEnergy != particle.mass.energy && neighboringParticleList.Count != 0)
        {
            // Iterate through neighboring particles
            foreach (Particle neigborParticle in neighboringParticleList)
            {
                // If their value is not equal to the average set it and set the flag
                if (neigborParticle.mass.energy != averageEnergy)
                {
                    neigborParticle.mass.SetEnergy(averageEnergy);

                    if (!neigborParticle.mass.isChanging)
                    {
                        neigborParticle.mass.isChanging = true;
                        newParticlesToAddToList.Add(neigborParticle);
                    }
                }
            }

            // Set the starting particle's energy to average value as well
            particle.mass.SetEnergy(averageEnergy);
        }
        else if (particle.mass.energy != 0.0f)
        {
            DissipateHeat(particle);
        }
        else
        {
            // The particle and it's neighbors are in equilibrium so remove it from the list
            particle.mass.isChanging = false;
            if (!particle.mass.isMoving)
            {
                // Particle needs to be put back into the static list
                MoveParticle(particles, particles, particle);
            }
            else
            {
                particles.Remove(particle);
            }

            i--;
        }
    }

    foreach (Particle newParticle in newParticlesToAddToList)
    {
        if (particles.Contains(newParticle))
        {
            continue;
        }

        if (newParticle.mass.isMoving)
        {
            CopyParticle(particles, particles, newParticle);
        }
        else
        {
            MoveParticle(particles, particles, newParticle);
        }
    }
}
public void StateChange(ObjectLibrary objectLibrary, float elementUse, GameObject gameObject)
{

    Particle particle = (Particle)gameObject;

    gameObject.mass.ChangeEnergy(elementUse / 2.0f);

    if (gameObject.mass.isChanging)
    {
        return;
    }
    else if (gameObject.mass.isMoving)
    {
        // Particle is in the dynamic list so we need to copy it to the Thermal list
        switch (particle.particleType)
        {
            case Particle.ParticleType.Air:
                {
                    objectLibrary.particleManager.air.CopyParticle(objectLibrary.particleManager.air.dynamicParticles,
                                                                     objectLibrary.particleManager.air.thermalParticles,
                                                                     particle);
                    break;
                }
            case Particle.ParticleType.Water:
                {
                    objectLibrary.particleManager.water.CopyParticle(objectLibrary.particleManager.water.dynamicParticles,
                                                                        objectLibrary.particleManager.water.thermalParticles,
                                                                        particle);
                    break;
                }
            case Particle.ParticleType.Earth:
                {
                    objectLibrary.particleManager.earth.CopyParticle(objectLibrary.particleManager.earth.dynamicParticles,
                                                                       objectLibrary.particleManager.earth.thermalParticles,
                                                                       particle);
                    break;
                }
        }
        gameObject.mass.isChanging = true;
    }
    else
    {
        // It's in the static list, so move the particle to the dynamic list
        switch (particle.particleType)
        {
            case Particle.ParticleType.Air:
                {
                    objectLibrary.particleManager.air.MoveParticle(objectLibrary.particleManager.air.staticParticles,
                                                                     objectLibrary.particleManager.air.thermalParticles,
                                                                     particle);
                    break;
                }
            case Particle.ParticleType.Water:
                {
                    objectLibrary.particleManager.water.MoveParticle(objectLibrary.particleManager.water.staticParticles,
                                                                        objectLibrary.particleManager.water.thermalParticles,
                                                                        particle);
                    break;
                }
            case Particle.ParticleType.Earth:
                {
                    objectLibrary.particleManager.earth.MoveParticle(objectLibrary.particleManager.earth.staticParticles,
                                                                       objectLibrary.particleManager.earth.thermalParticles,
                                                                       particle);
                    break;
                }
        }
        gameObject.mass.isChanging = true;
    }
}
public void UpdateLightPosition()
{
    // Light goes 180 degrees back and forth (never below)
    //if (Math.Abs(lightMoveAngle1) > Math.PI / 2.0f)
    //{
    //    LIGHT_MOVE_SPEED1 = -LIGHT_MOVE_SPEED1;
    //}
            
    // Light goes full 360 degrees
    // Update light source position
    if (lightMoveAngle1 < 0)
    {
        //reset to 2PI
        lightMoveAngle1 = Math.PI * 2.0f;
    }
    else if (lightMoveAngle1 > Math.PI * 2.0f)
    {
        //reset to zero
        lightMoveAngle1 = 0.0f;
    }
            
    // Update the angle of the light
    lightMoveAngle1 += LIGHT_MOVE_SPEED1;

    //Calculate the X and Y based on the angle
    float dx = (float)(lightDistance1 * Math.Sin(lightMoveAngle1));
    float dz = (float)(lightDistance1 * Math.Cos(lightMoveAngle1));
    lightInfo1.M11 = dx + (Statics.TerrainSettings.collisionMapSize / 2.0f);
    lightInfo1.M13 = dz +(Statics.TerrainSettings.collisionMapSize / 2.0f);
}
*/