public ProjectileProperties(Vector3 scale, Vector3 pbox, Vector3 cbox, bool existsIn2d, bool existsIn3d, int damage,
										float speed, bool gravity, SpriteSheet sprite, Effect death = null, double staminaCost = 0.0, double duration = -1.0) {
			this.scale = scale;
			this.pbox = pbox;
			this.cbox = cbox;
			this.existsIn2d = existsIn2d;
			this.existsIn3d = existsIn3d;
			this.damage = damage;
			this.speed = speed;
			this.gravity = gravity;
			this.staminaCost = staminaCost;
			this.duration = duration;
			this.sprite = sprite;
			this.deathAnim = death;
		}
		public Enemy(Player player, Vector3 location, Vector3 scale, Vector3 pbox, Vector3 cbox, bool existsIn2d, bool existsIn3d, int health, int damage, float speed,
						int AItype, ObjMesh mesh, MeshTexture texture, Effect death) : base() {
			_location = location;
            _scale = scale;
            _pbox = pbox;
            _cbox = cbox;
			_existsIn3d = existsIn3d;
			_existsIn2d = existsIn2d;
            _health = health;
            _damage = damage;
            _speed = speed;
            _alive = true;
            this.AItype = AItype;
            _hascbox = true;
            _type = 1;
            frozen = false;
            maxFreezeTime = 0.7;
            freezetimer = 0;
            attackspeed = 1;
            attacktimer = 0;
			this.player = player;
			_deathAnim = death;
            

			_mesh = mesh;
			_texture = texture;
			_sprite = null;
			_cycleNum = 0;
			_frameNum = 0;
			_is3dGeo = true;
			_animDirection = 1;

            velocity = new Vector3(0, 0, 0);
			accel = new Vector3(0, 0, 0);
			kbspeed = new Vector3(70, 100, 70);
			moving = false;
            doesGravity = true;

            CurrentAI = new Stack<Airoutine>();
            InitilizeAI();
		}
        public Enemy(Player player, Vector3 location, Vector3 scale, Vector3 pbox, Vector3 cbox, bool existsIn2d, bool existsIn3d, int health, int damage, float speed,
						int AItype, SpriteSheet sprite, Effect death, ProjectileProperties proj = null)
        {
			_location = location;
			_scale = scale;
            _pbox = pbox;
            _cbox = cbox;
			_existsIn3d = existsIn3d;
			_existsIn2d = existsIn2d;
            _health = health;
            _damage = damage;
            _speed = speed;
            _alive = true;
            this.AItype = AItype;
            _hascbox = true;
            _type = 1; // type one means this is an enemy
            frozen = false;
            freezetimer = 0;
            maxFreezeTime = 0.7;
            attackspeed = 1;
            attacktimer = 0;
			this.player = player;
			_deathAnim = death;

			_mesh = null;
			_texture = null;
			_sprite = sprite;
			projectile = proj;
            if (projectile != null)
                projectile.damage = damage;
			_frameNum = 0;
			_is3dGeo = false;
			_animDirection = 1;

            velocity = new Vector3(0, 0, 0);
            accel = new Vector3(0, 0, 0);
            doesGravity = true;

            CurrentAI = new Stack<Airoutine>();
            InitilizeAI();
		}
        /// <summary>
        /// Update, this gets called once every update frame
        /// </summary>
        /// <param name="e">FrameEventArgs from OpenTK's update</param>
		public override void Update(FrameEventArgs e) {
			//e = new FrameEventArgs(e.Time * 0.1);           
            //First deal with hardware input
            DealWithInput();

            if (musicenabled) {
                // Loop music when necessary
                if (levelMusic.CurrentSource.FileHasEnded) {
                    levelMusic.CurrentSource.FileHasEnded = false;
                    levelMusic.ReplayFile();
                }
            }
            //Next check if the player is dead. If he is, game over man
            if (player.health <= 0) {
                GameOverState GGbro = new GameOverState(menustate, eng, this);
                levelMusic.Stop();

				//Null all the lists to speed up garbage collection
				for(int i = objList.Count - 1; i >= 0; i--) { objList[i] = null; }
				for(int i = renderList.Count - 1; i >= 0; i--) { renderList[i] = null; }
				for(int i = collisionList.Count - 1; i >= 0; i--) { collisionList[i] = null; }
				for(int i = physList.Count - 1; i >= 0; i--) { physList[i] = null; }
				for(int i = aiList.Count - 1; i >= 0; i--) { aiList[i] = null; }
				for(int i = combatList.Count - 1; i >= 0; i--) { combatList[i] = null; }
				for(int i = backgroundList.Count - 1; i >= 0; i--) { backgroundList[i] = null; }
				for(int i = bossList.Count - 1; i >= 0; i--) { bossList[i] = null; }
				for(int i = bossRemoveList.Count - 1; i >= 0; i--) { bossRemoveList[i] = null; }
				for(int i = effectsList.Count - 1; i >= 0; i--) { effectsList[i] = null; }

                eng.ChangeState(GGbro);
				return;
            }

			//See if we need to trigger an event (like the end of the level or a boss)
			if(!bossMode && bossRegion.contains(player.location)) {
			    //Entered boss area - make changes to camera, etc
                enterBossMode();
			}
			if(endRegion.contains(player.location)) {
				//Finished level
				eng.ChangeState(new MainMenuState(eng)); //Later we should go to the next level when applicable
			}

            //do boss dies stuff, transition to next level?
            if (bossMode && (bossAI.gethealth() <= 0)) {
                bossAI.killBoss(this);
				bossMode = false;
                //transition to next level
                waitingToSwitchLevels = true;
            }

            if (waitingToSwitchLevels) {
                nextLevelTimer = nextLevelTimer + e.Time;
                if (nextLevelTimer > 3) {
                    loadNextLevel();//loads the next level
                    return;
                }
            }

			//Determine which screen region everything is in
			foreach(GameObject go in objList) {
				float dist = VectorUtil.dist(go.location, player.location);
				if(dist < 400.0) {
					go.screenRegion = GameObject.ON_SCREEN;
				} else if(dist > 500.0) {
					go.screenRegion = GameObject.OFF_SCREEN;
				}
				//If between the two distances, then leave as is
			}
            
            //handle death and despawning for everything else
			for(int i=combatList.Count-1; i>=0; i--) {
				CombatObject co = combatList[i];
                if (co.type == (int)CombatType.enemy) {
					if(co.health <= 0) {
						objList.Remove((GameObject)co);
						physList.Remove((PhysicsObject)co);
						collisionList.Remove((PhysicsObject)co);
						renderList.Remove((RenderObject)co);
						aiList.Remove((AIObject)co);
						combatList.Remove(co);

						Effect death = new Effect(co.location, co.deathAnim);
						objList.Add(death);
						renderList.Add(death);
						effectsList.Add(death);
                    }
				} else if(co.type == (int)CombatType.projectile ||
						  co.type == (int)CombatType.squish ||
						  co.type == (int)CombatType.grenade) {
					if(co.health <= 0 || co.ScreenRegion == GameObject.OFF_SCREEN) {
						objList.Remove((GameObject)co);
						physList.Remove((PhysicsObject)co);
						collisionList.Remove((PhysicsObject)co);
						renderList.Remove((RenderObject)co);
						combatList.Remove(co);

						if(co.deathAnim != null) {
							Effect death = new Effect(co.location, co.deathAnim);
							objList.Add(death);
							renderList.Add(death);
							effectsList.Add(death);
						}
                    }
                }
            }

			//If the camera is transitioning, everything else is paused
            if (!camera.isInTransition) {
				//Deal with everyone's acceleration, run AI on enemies
				player.updateState(enable3d, eng.Keyboard, e.Time);
                if (!bossMode) {
                    foreach (AIObject aio in aiList) {
                        if (aio.ScreenRegion == GameObject.ON_SCREEN) {
                            if (aienabled) {
                                aio.aiUpdate(e.Time, this, player.location, enable3d, physList);
                            }
                        }
                    }
                }
                else { //if we are in boss mode then route control to the bosses code instead of the enemy updates
                    //update the boss
                    bossAI.update(e.Time, this, player.location, enable3d);
                }

				//Now that everyone's had a chance to accelerate, actually
				//translate that into velocity and position
				if(enable3d) {
                    player.physUpdate3d(e.Time, physList);
					for(int i = collisionList.Count - 1; i >= 0; i--) {
						collisionList[i].physUpdate3d(e.Time, physList);
					}
				} else {
					player.physUpdate2d(e.Time, physList);
					for(int i = collisionList.Count - 1; i >= 0; i--) {
						collisionList[i].physUpdate2d(e.Time, physList);
					}
				}

				//These updates must be last
				foreach(Background b in backgroundList) {
					b.UpdatePositionX(player.deltax);
				}
			}
			//Update the camera whether we're transitioning or not
			camera.Update(e.Time);
		}
		public Effect(Vector3 location, Effect template)
			: this(template.playstate, location, template.scale, template.existsIn2d, template.existsIn3d, template.billboards, template.sprite) { }