public MirrorPlayer(GameController game)
            : base(game)
        {
            this.game = game;

            // Model
            this.colorDefault = Constants.noColor;
            this.color = colorDefault;
            this.texturename = null;
            this.modelname = Constants.model_fish;
            getSizeRatioAndTransformation();
            this.type = GameObjectType.MirrorPlayer;

            // Stats
            hp = game.player.hp;
            mass = hp / size_ratio;
            maxAttackDelay = 0;
            maxHitDelay = 0;
            maxRotationSpeed = Constants.playerRotationSpeed;
            attackingAngle = Constants.attackingAngle;
            timeInBetweenShots = 100;

            this.pos = game.player.pos;
            this.movementSpeed = game.player.movementSpeed;
            this.acceleration = game.player.acceleration;
            this.accX = acceleration * ((float)Math.Cos(game.player.rotationAngle));
            this.accY = acceleration * ((float)Math.Sin(game.player.rotationAngle));
            this.pos_offset_x = ((float)game.random.NextDouble() * this.mass * 10 - this.mass*5);
            this.pos_offset_y = ((float)game.random.NextDouble() * this.mass * 10 - this.mass * 5);
            this.pos.X = game.player.pos.X + pos_offset_x;
            this.pos.Y = game.player.pos.Y + pos_offset_y;
            this.damage = game.player.damage/2;

            // Gamelogic
            isCollidable = false;

            refreshModel(color);

            // Follow a target
            target = null;

            game.Add(this);
        }
        // Handle a collision with a specific object
        public void rebound(float timeDelta, GameObject obj)
        {
            if (!this.colossal)
            {
                float vtx = accX * timeDelta * Constants.bounciness;
                float vty = accY * timeDelta * Constants.bounciness;

                this.velX -= vtx * obj.mass / (this.mass + obj.mass) * Constants.elasticModulus;
                this.velY -= vty * obj.mass / (this.mass + obj.mass) * Constants.elasticModulus;
            }
        }
        // Check if a specific object is within attack region
        public bool isFacing(GameObject obj)
        {
            // Get the angle between the two objects
            float angle = Methods.getAngle(this.pos, obj.pos);
            float[] diff = new float[2];

            diff[0] = Methods.absDiff(angle, this.rotationAngle);
            diff[1] = Methods.absDiff(angle + (float)Math.PI * 2, this.rotationAngle);

            if (diff[0] < attackingAngle || diff[1] < attackingAngle)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        // Calculate the squared distance from a specific object
        public float getCollisionDistanceSquared(GameObject obj)
        {
            float this_weighted_radius = this.mass / 2f * this.size_ratio / Constants.size_ratio_cube;
            float obj_weighted_radius = obj.mass / 2f * obj.size_ratio / Constants.size_ratio_cube;

            float sumRadiusSquared = (this_weighted_radius + obj_weighted_radius) * (this_weighted_radius + obj_weighted_radius);
            float collisionDistance = Methods.lengthSquared3(this.pos, obj.pos) - sumRadiusSquared;

            return collisionDistance;
        }
        // Check collision with a specific object and update the collision state
        public void checkCollision(GameObject obj, float timeDelta)
        {
            float collisionDistance = getCollisionDistanceSquared(obj);
            //float mag = Math.Min(Methods.magnitudeSquared(velX * timeDelta, velY * timeDelta), Methods.magnitudeSquared(obj.velX * timeDelta, obj.velY * timeDelta));

            if (collisionDistance < 0)
            {
                collisionState = CollisionState.Overlapping;
            }
            //else if (collisionDistance < mag)
            //{
            //    collisionState = CollisionState.Touching;
            //}
            else
            {
                collisionState = CollisionState.None;
            }
        }
        // Attack a specific object, amount is the percentage damage dealt (1 = 100%)
        public void attack(GameObject obj, float amount)
        {
            if (this == game.player) game.scoreController.addToScore(Constants.score_attacks);

            //game.soundController.PlaySound("hit1.wav", 0.7);

            float addedHp;

            // Will need to recreate models since hp has changed
            modelNeedsRefresh = true;
            obj.modelNeedsRefresh = true;

            // Update attack delay and hit delay counters
            attackDelayCounter = maxAttackDelay;
            obj.hitDelayCounter = obj.maxHitDelay;

            if (obj.hp - (damage * amount) <= 0)       // Remove victim if its hp reaches 0
            {
                addedHp = obj.hp;           // Add victim's remaining hp to attacker
                obj.hp = 0;
                obj.kill();
            }
            else
            {
                addedHp = damage * amount;           // Otherwise add hp to attacker equal to attacker's damage
                obj.hp -= addedHp;           // Remove HP from victim
                obj.damage -= addedHp * Constants.added_damage_ratio;       // decrease victim's damage
            }

            // HP and Damage addition is not applicable for survival units or projectiles
            if (this.type != GameObjectType.EnemySurvival &&
                this.type != GameObjectType.ProjectilePlayer &&
                this.type != GameObjectType.ProjectileEnemy &&
                this.type != GameObjectType.FieldPlayer)
            {
                hp += addedHp * Constants.added_hp_ratio;                   // Update hp, survival units can't gain hp
                damage += addedHp * Constants.added_damage_ratio;           // Increase attack's damage
            }

            // Limit how low a unit's damage can go
            if (obj.damage < Constants.minimumDamage) obj.damage = Constants.minimumDamage;
        }
        public void applyVortex(GameObject target, float range)
        {
            accX = 0;
            accY = 0;

            if (target.isAlive)
            {
                float diffX = pos.X - target.pos.X;
                float diffY = pos.Y - target.pos.Y;

                if (diffX < range + target.mass && diffY < range + target.mass)
                {
                    if (diffX >= 0) accX = -Constants.vortexAccel;
                    else accX = Constants.vortexAccel;

                    if (diffY >= 0) accY = -Constants.vortexAccel;
                    else accY = Constants.vortexAccel;
                }
            }
        }
        public Projectile(GameObject obj, GameController game, float variance, Vector4 color, float damage, bool randomDirection, bool homing)
            : base(game)
        {
            this.pos = obj.pos;
            this.game = game;

            if (obj.type == GameObjectType.Player || obj.type == GameObjectType.MirrorPlayer)
            {
                this.type = GameObjectType.ProjectilePlayer;
            }
            else
            {
                this.type = GameObjectType.ProjectileEnemy;
            }

            // Model
            // projectile color - default blue, otherwise copies the shooter's color
            if (obj.colorDefault == Constants.noColor) this.colorDefault = Constants.blue;
            else this.colorDefault = obj.colorDefault;

            // Init values
            this.colorDefault = color;
            this.color = colorDefault;
            this.texturename = null;
            this.modelname = Constants.model_projectile;
            getSizeRatioAndTransformation();
            this.showLighting = false;

            // Stats
            hp = Constants.projectile_base_size + obj.hp/10;   // Here HP and MASS just determines size of the projectile
            mass = hp / size_ratio;
            this.damage = damage;
            this.maxRotationSpeed = Constants.projectileRotationSpeed;

            // Set base speed and accel
            this.movementSpeed = Constants.projectile_speed;
            this.acceleration = Constants.projectile_accel;

            // Initial accel and velocity is in the direction of the shooter
            this.accX = acceleration * ((float)Math.Cos(obj.rotationAngle));
            this.accY = acceleration * ((float)Math.Sin(obj.rotationAngle));
            this.velX = obj.velX;
            this.velY = obj.velY;

            // Random projectile direction if applicable
            if (randomDirection)
            {
                accX = accX + ((float)game.random.NextDouble() * variance * 2 - variance);
                accY = accY + ((float)game.random.NextDouble() * variance * 2 - variance);
            }

            this.pos.X += ((float)Math.Cos(obj.rotationAngle)) * obj.mass / 2f;
            this.pos.Y += ((float)Math.Sin(obj.rotationAngle)) * obj.mass / 2f;

            // Gamelogic
            isCollidable = false;
            isAffectedByVortex = false;
            this.homing = homing;

            if (homing) lifeTime = Constants.projectile_lifetime + 400;
            else lifeTime = Constants.projectile_lifetime;

            refreshModel(color);

            game.Add(this);
        }
        public void seekTarget()
        {
            accX = 0;
            accY = 0;

            float diffX = 10000;
            float diffY = 10000;

            if (target == null)
            {

                foreach (var obj in game.gameObjects)
                {
                    if (type == GameObjectType.ProjectilePlayer)
                    {
                        if (obj.type == GameObjectType.Enemy || obj.type == GameObjectType.Boss || obj.type == GameObjectType.EnemySurvival)
                        {
                            float x = pos.X - obj.pos.X;
                            float y = pos.Y - obj.pos.Y;

                            if (Math.Abs(diffX) > Math.Abs(x)) { diffX = x; target = obj; }
                            if (Math.Abs(diffY) > Math.Abs(y)) { diffY = y; target = obj; }
                        }
                    }
                    else
                    {
                        if (obj.type == GameObjectType.Player || obj.type == GameObjectType.MirrorPlayer)
                        {
                            float x = pos.X - obj.pos.X;
                            float y = pos.Y - obj.pos.Y;

                            if (Math.Abs(diffX) > Math.Abs(x)) { diffX = x; target = obj; }
                            if (Math.Abs(diffY) > Math.Abs(y)) { diffY = y; target = obj; }
                        }
                    }
                }
            }
            else
            {
                diffX = pos.X - target.pos.X;
                diffY = pos.Y - target.pos.Y;
            }

            if (diffX >= 0) accX = -acceleration;
            else accX = acceleration;

            if (diffY <= 0) accY = acceleration;
            else accY = -acceleration;
        }
        public void keepNearPlayer()
        {
            float diffX = pos.X - game.player.pos.X;
               float diffY = pos.Y - game.player.pos.Y;

               if (Math.Abs(diffX) > 5 && Math.Abs(diffY) > 5)
               {
                   target = game.player;
               }
               else
               {
                   if (target == game.player) target = null;
               }
        }
        public void moveTowardsEnemy()
        {
            accX = 0;
            accY = 0;

            float diffX = 10000;
            float diffY = 10000;

            if (target == null)
            {

                foreach (var obj in game.gameObjects)
                {
                    if (obj.type == GameObjectType.Enemy || obj.type == GameObjectType.Boss)
                    {
                        float x = pos.X - obj.pos.X;
                        float y = pos.Y - obj.pos.Y;

                        if (Math.Abs(diffX) > Math.Abs(x)) { diffX = x; target = obj; }
                        if (Math.Abs(diffY) > Math.Abs(y)) { diffY = y; target = obj; }
                    }
                }
            }
            else
            {
                diffX = pos.X - target.pos.X;
                diffY = pos.Y - target.pos.Y;
            }

                if (Math.Abs(diffX) < Constants.mirrorImageRange && Math.Abs(diffY) < Constants.mirrorImageRange)
                {
                    if (diffX >= 0) accX = -acceleration;
                    else accX = acceleration;

                    if (diffY <= 0) accY = acceleration;
                    else accY = -acceleration;
                }
        }