public class SoundSource {
	/** Buffers for transferring data */
	private FloatBuffer posBuf, velBuf;
	
	/** Handle to use for source */
	private int handle;
	
	/** If this is set to true, then this source should be removed as soon as it's done playing its sound */
	public boolean removeFlag = false;
	
	private boolean loop;
	
	private float pitch, currentGain;
	
	private float defaultGain;
	
	/**
	 * Create a new source for sound
	 * @param sound Sound to play
	 * @param loop Whether or not to loop sound
	 * @param location Initial location
	 * @param velocity Initial velocity
	 */
	public SoundSource(Sounds sound, boolean loop, Vector3f location, Vector3f velocity){
		// create buffers
		posBuf = BufferUtils.createFloatBuffer(3);
		velBuf = BufferUtils.createFloatBuffer(3);
		
		// generate handle
		IntBuffer buf = BufferUtils.createIntBuffer(1);
		AL10.alGenSources(buf);
		handle = buf.get(0);
		
		// set location and speed
		setLocation(location);
		setVelocity(velocity);
		
		this.pitch = sound.pitch;
		this.currentGain = sound.gain;
		this.defaultGain = sound.gain;
		this.loop = loop;
		
		// set values
		AL10.alSourcei(handle, AL10.AL_BUFFER, sound.getHandle());
		AL10.alSourcef(handle, AL10.AL_PITCH, pitch);
		AL10.alSourcei(handle, AL10.AL_LOOPING, loop ? AL10.AL_TRUE : AL10.AL_FALSE);
		
		setGain(Audio.isMuted() ? 0.0f : currentGain);
		
		// all sound sources are added to this list so they can be deleted when the game exits
		Audio.soundSources.add(this);
	}
	
	/**
	 * Sets the pitch of the sound
	 * @param pitch New pitch
	 */
	public void setPitch(float pitch){
		this.pitch = pitch;
		AL10.alSourcef(handle, AL10.AL_PITCH, pitch);
	}
	
	/**
	 * @return Current pitch
	 */
	public float getPitch(){
		return pitch;
	}
	
	/**
	 * @return Current gain
	 */
	public float getCurrentGain(){
		return currentGain;
	}
	
	/**
	 * @return Default gain
	 */
	public float getDefaultGain(){
		return defaultGain;
	}
	
	/**
	 * Sets the gain of the sound
	 * @param gain New gain (0 = none, 0.5 = 50%, 1 = 100%, 2 = 200% etc.)
	 */
	public void setGain(float gain){
		this.currentGain = gain;
		AL10.alSourcef(handle, AL10.AL_GAIN,  Audio.currentVolume() * gain);
	}
	
	/**
	 * Sets the location of the sound
	 * @param location New location
	 */
	public void setLocation(Vector3f location){
		posBuf.clear();
		posBuf.put(location.x);
		posBuf.put(location.y);
		posBuf.put(location.z);
		posBuf.rewind();
		AL10.alSource (handle, AL10.AL_POSITION, posBuf);
	}
	
	/**
	 * Sets the velocity of the sound
	 * @param velocity New velocity
	 */
	public void setVelocity(Vector3f velocity){
		velBuf.clear();
		velBuf.put(velocity.x);
		velBuf.put(velocity.y);
		velBuf.put(velocity.z);
		velBuf.rewind();
		AL10.alSource (handle, AL10.AL_VELOCITY, velBuf);
	}

	/**
	 * Plays the sound
	 */
	public void playSound(){
		AL10.alSourcePlay(handle);
	}
	
	/**
	 * Stops sound
	 */
	public void stopSound(){
		AL10.alSourceStop(handle);
	}
	
	/**
	 * Pauses sound
	 */
	public void pauseSound(){
		AL10.alSourcePause(handle);
	}
	
	/**
	 * @return Whether or not this sound is looping
	 */
	public boolean isLooping(){
		return loop;
	}
	
	/**
	 * @param loop Whether or not this sound source is looping
	 */
	public void setLooping(boolean loop){
		this.loop = loop;
		AL10.alSourcei(handle, AL10.AL_LOOPING, loop ? AL10.AL_TRUE : AL10.AL_FALSE);
	}
	
	/**
	 * Rewinds sound
	 */
	public void rewindSound(){
		AL10.alSourceRewind(handle);
	}
	
	
	/**
	 * Queues a buffer
	 * @param buffer Buffer to queue
	 */
	public void queueBuffer(int buffer){
		AL10.alSourceQueueBuffers(handle, buffer);
	}
	
	/**
	 * Un-queues buffers
	 */
	public void unqueueBuffers(){
		AL10.alSourceUnqueueBuffers(handle);
	}
	
	/**
	 * Should be called when removing source
	 */
	public void shutdown(){
		AL10.alDeleteSources(handle);
	}
	
	/**
	 * @return Whether or not a sound is being played right now
	 */
	public boolean isPlaying(){
		int stat = AL10.alGetSourcei(handle, AL10.AL_SOURCE_STATE);
		if(stat == AL10.AL_PLAYING)
			return true;
		else
			return false;
	}
} public class DynamicEntity extends Entity {
	/** the rigid body for this entity */
	public RigidBody rigidBody;

	/** the model to use for this entity */
	public Model model;

	/**
	 * if this is true, the next time the entity is updated it is removed from
	 * the world
	 */
	public boolean removeFlag = false;

	/**
	 * Overloaded constructor
	 */
	public DynamicEntity(Vector3f location, Quaternion rotation, Model model,
			float mass, float restitution) {
		this(location, rotation, model, mass, restitution,
				CollisionTypes.NOTHING, CollisionTypes.NOTHING);
	}

	public DynamicEntity(Vector3f location, Quaternion rotation, Models model,
			float mass, float restitution) {
		this(location, rotation, model.getModel(), mass, restitution,
				CollisionTypes.NOTHING, CollisionTypes.NOTHING);
	}

	public DynamicEntity(Vector3f location, Quaternion rotation, Models model,
			float mass, float restitution, short collisionGroup,
			short collidesWith) {
		this(location, rotation, model.getModel(), mass, restitution,
				collisionGroup, collidesWith);
	}

	/**
	 * Creates the entity and adds it to the dynamics world (but NOT to
	 * Entities.dynamicEntities)
	 * 
	 * @param location
	 *            Initial location of the entity
	 * @param rotation
	 *            Initial rotation of the entity (<i>Be careful!</i> If the
	 *            quaternion isn't right, i.e. not normalized, funny things will
	 *            happen)
	 * @param model
	 *            The {@link Model} for the entity
	 * @param mass
	 *            The mass for the entity
	 * @param restitution
	 *            The restitution (bounciness) of the entity
	 * @param collisionGroup
	 *            which group from {@link CollisionTypes} this entity belongs to
	 */
	public DynamicEntity(Vector3f location, Quaternion rotation, Model model,
			float mass, float restitution, short collisionGroup,
			short collidesWith) {
		// see Entity for location and rotation
		this.location = location;
		this.rotation = rotation;
		this.model = model;

		// the transform to use for putting the entity into the world
		Transform transform = new Transform();
		transform.setRotation(new Quat4f(rotation.x, rotation.y, rotation.z,
				rotation.w));
		transform.origin.set(location.x, location.y, location.z);
		DefaultMotionState defaultState = new DefaultMotionState(transform);

		// location to use for the entity (need a javax.vecmath Vector3f instead
		// of the given org.lwjgl.util.vector Vector3f
		javax.vecmath.Vector3f loca = new javax.vecmath.Vector3f(location.x,
				location.y, location.z);

		// the collision shape is made when the model is made
		CollisionShape shape = model.getCollisionShape();

		// no initial fall inertia (it isn't vital to set this)
		javax.vecmath.Vector3f fallInertia = new javax.vecmath.Vector3f(0.0f,
				0.0f, 0.0f);
		shape.calculateLocalInertia(mass, fallInertia);

		// create the rigid body based on all the stuff we've grabbed
		RigidBodyConstructionInfo rigidBodyCI = new RigidBodyConstructionInfo(
				mass, defaultState, shape, loca);
		rigidBodyCI.restitution = restitution;
		rigidBody = new RigidBody(rigidBodyCI);

		// set the pointer so the entity can be updated (see
		// DynamicEntityCallback)
		rigidBody.setUserPointer(this);

		// finally, add it to the world
		if (collisionGroup != CollisionTypes.NOTHING
				&& collidesWith != CollisionTypes.NOTHING)
			Physics.dynamicsWorld.addRigidBody(rigidBody, collisionGroup,
					collidesWith);
		else
			Physics.dynamicsWorld.addRigidBody(rigidBody);
	}
	
	public DynamicEntity(Vector3f location, Quaternion rotation, CollisionShape shape,
			float mass, float restitution, short collisionGroup,
			short collidesWith) {
		// see Entity for location and rotation
		this.location = location;
		this.rotation = rotation;

		// the transform to use for putting the entity into the world
		Transform transform = new Transform();
		transform.setRotation(new Quat4f(rotation.x, rotation.y, rotation.z,
				rotation.w));
		transform.origin.set(location.x, location.y, location.z);
		DefaultMotionState defaultState = new DefaultMotionState(transform);

		// location to use for the entity (need a javax.vecmath Vector3f instead
		// of the given org.lwjgl.util.vector Vector3f
		javax.vecmath.Vector3f loca = new javax.vecmath.Vector3f(location.x,
				location.y, location.z);

		// no initial fall inertia (it isn't vital to set this)
		javax.vecmath.Vector3f fallInertia = new javax.vecmath.Vector3f(0.0f,
				0.0f, 0.0f);
		shape.calculateLocalInertia(mass, fallInertia);

		// create the rigid body based on all the stuff we've grabbed
		RigidBodyConstructionInfo rigidBodyCI = new RigidBodyConstructionInfo(
				mass, defaultState, shape, loca);
		rigidBodyCI.restitution = restitution;
		rigidBody = new RigidBody(rigidBodyCI);

		// set the pointer so the entity can be updated (see
		// DynamicEntityCallback)
		rigidBody.setUserPointer(this);

		// finally, add it to the world
		if (collisionGroup != CollisionTypes.NOTHING
				&& collidesWith != CollisionTypes.NOTHING)
			Physics.dynamicsWorld.addRigidBody(rigidBody, collisionGroup,
					collidesWith);
		else
			Physics.dynamicsWorld.addRigidBody(rigidBody);
	}

	@Override
	/**
	 * Simple as possible drawing call. This assumes that it's called when the entity's location and rotation have already been applied to the modelview matrix.
	 */
	public void draw() {
		model.getTexture().texture().bind();
		model.render();
	}

	/**
	 * Draws the physics debug info for this entity. Should be called before
	 * rotations are applied.
	 */
	public void drawPhysicsDebug() {
		Transform worldTransform = new Transform();
		rigidBody.getWorldTransform(worldTransform);

		CollisionShape shape = model.getCollisionShape();

		Physics.dynamicsWorld.debugDrawObject(worldTransform, shape,
				new javax.vecmath.Vector3f(0.0f, 0.0f, 0.0f));
	}
	
	public ClosestRayResultCallback rayTest(Vector3f direction){
		// rotate the direction we want to test so that it's realtive to the entity's rotation
		Vector3f endRotated = QuaternionHelper.rotateVectorByQuaternion(direction, rotation);
		Vector3f endAdd = new Vector3f();
		// add the rotated direction to the current location to get the end vector
		Vector3f.add(location, endRotated, endAdd);
		
		javax.vecmath.Vector3f start = new javax.vecmath.Vector3f(location.x, location.y, location.z);
		javax.vecmath.Vector3f end = new javax.vecmath.Vector3f(endAdd.x, endAdd.y, endAdd.z);
		
		ClosestRayResultCallback callback = new ClosestRayResultCallback(start, end);
		Physics.dynamicsWorld.rayTest(start, end, callback);
		
		return callback;
	}

	@Override
	/**
	 * Removes the rigid body from the dynamics world it's in
	 */
	public void cleanup() {
		removeFlag = true;
	}

	@Override
	/**
	 * Update the dynamic entity
	 * NOTE: If you're making your own class that extends DynamicEntity,
	 * you need to override this method!
	 */
	public void update(float timeStep){};
} public class Entities {
	/** player instance */
	public static Player player;
	/** camera instance */
	public static Camera camera;
	/** the skybox */
	public static Skybox skybox;

	/** all the current passive entities */
	public static ArrayList<Entity> passiveEntities = new ArrayList<Entity>(10), passiveEntitiesToAdd = new ArrayList<Entity>(10), passiveEntitiesToRemove = new ArrayList<Entity>(10);
	/** all the dynamic entities */
	public static ArrayList<DynamicEntity> dynamicEntities = new ArrayList<DynamicEntity>(100);
	/** all the current lights */
	public static ArrayList<Light> lights = new ArrayList<Light>(8), lightsToAdd = new ArrayList<Light>(8), lightsToRemove = new ArrayList<Light>(8);
	
	/**
	 * Updates everything that doesn't get updated by the {@link DynamicEntityCallback}
	 * @param timeStep How much time has passed since the last tick (passed in from DynamicEntityCallback)
	 */
	public static void updateAll(float timeStep){
		/*
		 * Since passiveEntities and lights all get updated by iterating through their lists,
		 * directly adding and removing  from the list can cause a ConcurrentModificationException.
		 * Everything in DynamicEntities is updated in the DynamicEntityCallback, and as such the list
		 * in here is used solely for rendering. So adds and removes can be done directly on the
		 * list.
		 */
		if(!passiveEntitiesToRemove.isEmpty()){
			for(Entity ent : passiveEntitiesToRemove)
				passiveEntities.remove(ent);
			passiveEntitiesToRemove.clear();
		}
		
		if(!passiveEntitiesToAdd.isEmpty()){
			for(Entity ent : passiveEntitiesToAdd)
				passiveEntities.add(ent);
			passiveEntitiesToAdd.clear();
		}
		
		if(!lightsToRemove.isEmpty()){
			for(Light l : lightsToRemove)
				lights.remove(l);
			lightsToRemove.clear();
		}
		
		if(!lightsToAdd.isEmpty()){
			for(Light l : lightsToAdd)
				lights.add(l);
			lightsToAdd.clear();
		}
		
		camera.update(timeStep);
		skybox.update(timeStep);
		
		for(Entity ent : passiveEntities)
			ent.update(timeStep);
		
		for(Light l : lights)
			l.update(timeStep);
	}
	
	/**
	 * Adds the given DynamicEntity to the rendering world
	 * @param ent Entity to add
	 */
	public static void addDynamicEntity(DynamicEntity ent){
		dynamicEntities.add(ent);
	}
	
	/**
	 * Adds the given Entity to the rendering world
	 * @param ent Entity to add
	 */
	public static void addPassiveEntity(Entity ent){
		passiveEntitiesToAdd.add(ent);
	}
	
	/**
	 * Adds the given Light to the rendering world
	 * @param light Light to add
	 */
	public static void addLight(Light light){
		lightsToAdd.add(light);
	}
	
	/**
	 * Removes the given DynamicEntity from the rendering world
	 * @param ent Entity to remove
	 */
	public static void removeDynamicEntity(DynamicEntity ent){
		dynamicEntities.remove(ent);
	}
	
	/**
	 * Removes the given Entity from the rendering world
	 * @param ent Entity to remove
	 */
	public static void removePassiveEntity(Entity ent){
		passiveEntitiesToRemove.add(ent);
	}
	
	/**
	 * Removes the given Light from the rendering world
	 * @param light Light to remove
	 */
	public static void removeLight(Light l){
		lightsToRemove.add(l);
	}

	/**
	 * @return Whether or not there are any entities at the moment
	 */
	public static boolean entitiesExist() {
		return !passiveEntities.isEmpty() || !dynamicEntities.isEmpty() || player != null;
	}

	/**
	 * Gets the distance between two vectors
	 * 
	 * @param first
	 *            The first vector
	 * @param second
	 *            The second vector
	 * @return The distance between the two vectors
	 */
	public static float distance(Vector3f first, Vector3f second) {
		float xDist = first.x - second.x;
		float yDist = first.y - second.y;
		float zDist = first.z - second.z;

		float xSqr = xDist * xDist;
		float ySqr = yDist * yDist;
		float zSqr = zDist * zDist;

		double total = (double) (xSqr + ySqr + zSqr);

		return (float) Math.sqrt(total);
	}

	/**
	 * Delete all of the entities
	 */
	public static void cleanup() {
		for(Entity ent : passiveEntities){
			ent.cleanup();
		}
		
		for(DynamicEntity ent : dynamicEntities){
			ent.cleanup();
		}
		player = null;
		camera = null;
		passiveEntities.clear();
		dynamicEntities.clear();
		lights.clear();
	}
} public abstract class Entity {
	/** the entity's current location */
	public Vector3f location;

	/** quaternion representing rotation */
	public Quaternion rotation;

	/** type, used for lots of things */
	public String type;

	/**
	 * Entity constructor
	 */
	public Entity() {
		location = new Vector3f(0.0f, 0.0f, 0.0f);
		rotation = new Quaternion(1.0f, 0.0f, 0.0f, 1.0f);
		type = "entity";
	}

	/**
	 * Updates this entity
	 */
	public abstract void update(float timeStep);

	/**
	 * Draws this entity
	 */
	public abstract void draw();

	/**
	 * Have the entity provide any necessary cleanup
	 */
	public abstract void cleanup();
} public class Emitter{
	/** All the particles */
	private ArrayList<Particle> particles;
	
	/** Texture to use for drawing particle */
	private Textures particleTex;
	
	/** Used to preserve modelview */
	private static Matrix4f oldModelView = new Matrix4f();
	
	/** Box, for drawing particle */
	private static Box2D box = new Box2D(1.0f, 1.0f, Textures.FIRE.texture());
	
	/** The Entity that the particles are coming from */
	private Entity following;
	
	/** Offset from the center of the Entity being followed */
	private Vector3f offset;
	
	/** How much to vary the particle generation on each axis */
	private Vector3f locationVariance, velocityVariance;
	
	/** For generating random position offsets */
	private Random randy;
	
	/** Whether or not this emitter is emitting particles */
	public boolean active;
	
	/** Controls how fast the emitter emits */
	private float emitSpeed, timeSinceEmission;
	
	/** How many particles to emit per emission */
	private int particlesPerEmission;
	
	/** How long each particle lives for */
	private float particleTTLVariance;
	
	/**
	 * An object for emitting particles!
	 * @param following Entity to emit particles from
	 * @param particleTex Texture to use for Entities
	 * @param offset Offset from center of following entity
	 * @param emitSpeed How often to emit particles
	 * @param particlesPerEmission Particles to emit per emission
	 */
	public Emitter(Entity following, Textures particleTex, Vector3f offset, Vector3f locationVariance, Vector3f velocityVariance, float emitSpeed, int particlesPerEmission, float particleTTLVariance){
		this.following = following;
		this.particleTex = particleTex;
		this.offset = offset;
		this.locationVariance = locationVariance;
		this.velocityVariance = velocityVariance;
		this.emitSpeed = emitSpeed;
		this.particlesPerEmission = particlesPerEmission;
		this.particleTTLVariance = particleTTLVariance;
		particles = new ArrayList<Particle>();
		this.active = true;
		
		// seed with the time
		randy = new Random(System.nanoTime());
	}

	/**
	 * Updates all the Emitter's particles
	 * @param timeStep Amount of time since last update
	 */
	public void update(float timeStep) {
		// to avoid concurrentmodificationexception
		ArrayList<Particle> deleteList = new ArrayList<Particle>();
		
		// update particles and check for remove flag
		for(Particle p : particles){
			p.update(timeStep);
			if(p.removeFlag)
				deleteList.add(p);
		}
		
		// remove necessary particles
		if(!deleteList.isEmpty()){
			for(Particle p : deleteList)
				this.removeParticle(p);
		}
		
		// emit if the emitter is active
		if(active){
			// only emit if it's been enough time
			timeSinceEmission += timeStep;
			if(timeSinceEmission >= emitSpeed){
				// emit however many particles
				for(int i = 0; i < particlesPerEmission; i++)
					emitParticle();
				// reset counter
				timeSinceEmission = 0.0f;
			}
		}
	}
	
	/**
	 * @param p Particle to add to emitter
	 */
	public void addParticle(Particle p){
		particles.add(p);
	}
	
	/**
	 * @param p Particle to add to emitter
	 * @param index Index to add particle to
	 */
	public void addParticle(Particle p, int index){
		particles.add(index, p);
	}
	
	/**
	 * @param p Particles to add to emitter
	 */
	public void addParticles(ArrayList<Particle> p){
		particles.addAll(p);
	}
	
	/**
	 * @param p Remove particle from emitter
	 */
	public void removeParticle(Particle p){
		particles.remove(p);
	}
	
	/**
	 * @param index Index of particle to remove from emitter
	 */
	public void removeParticle(int index){
		particles.remove(index);
	}
	
	/**
	 * @param p Particles to remove
	 */
	public void removeParticles(ArrayList<Particle> p){
		particles.removeAll(p);
	}
	
	/**
	 * @return Whether or not this emitter has any particles
	 */
	public boolean hasParticles(){
		return !this.particles.isEmpty();
	}

	/**
	 * Draws all the particles associated with this Emitter
	 */
	public void draw() {
		// disable lighting and enable blending
		Render3D.program.setUniform("Light.LightEnabled", false);
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		
		// get the revese rotation of what we're following
		Quaternion revQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		this.following.rotation.negate(revQuat);
		
		// bind texture and array handle
		particleTex.texture().bind();
		GL30.glBindVertexArray(box.getVAOHandle());
		
		// draw all particles
		for(Particle p : particles){
			// amount to translate
			float transx = this.following.location.x - p.location.x;
			float transy = this.following.location.y - p.location.y;
			float transz = this.following.location.z - p.location.z;
			
			// save the modelview before we manipulate it
			oldModelView.load(Render3D.modelview);{
				Matrix4f.mul(Render3D.modelview, QuaternionHelper.toMatrix(revQuat), Render3D.modelview);
				// translate and scale the modelview
				Render3D.modelview.translate(new Vector3f(transx, transy, transz));
				// billboard the particle
				Matrix4f.mul(Render3D.modelview, QuaternionHelper.toMatrix(Entities.camera.rotation), Render3D.modelview);
				Render3D.modelview.scale(new Vector3f(p.width, p.height, 1.0f));
				Render3D.program.setUniform("ModelViewMatrix", Render3D.modelview);

				// draw the particle
				GL11.glDrawArrays(GL11.GL_QUADS, 0, 4);
			}Render3D.modelview.load(oldModelView);
		}
		
		// re-enable lighting
		Render3D.program.setUniform("Light.LightEnabled", true);
		GL11.glDisable(GL11.GL_BLEND);
	}
	
	/**
	 * Emit a single particle from this emitter
	 */
	public void emitParticle(){
		// offset from center of emitter
		float randXOffset, randYOffset, randZOffset;
		
		if(randy.nextBoolean()) 
			randXOffset = randy.nextFloat() * locationVariance.x;
		else
			randXOffset = randy.nextFloat() * -locationVariance.x;
		
		if(randy.nextBoolean()) 
			randYOffset = randy.nextFloat() * locationVariance.y;
		else
			randYOffset = randy.nextFloat() * -locationVariance.y;
		
		if(randy.nextBoolean()) 
			randZOffset = randy.nextFloat() * locationVariance.z;
		else
			randZOffset = randy.nextFloat() * -locationVariance.z;
		
		// place the particle at the offset for the emitter and rotate the location
		Vector3f behind = QuaternionHelper.rotateVectorByQuaternion(new Vector3f(randXOffset + offset.x, randYOffset + offset.y, randZOffset + offset.z), this.following.rotation);
		
		// add the rotated vector to the location
		Vector3f loc = new Vector3f(this.following.location.x + behind.x, this.following.location.y + behind.y, this.following.location.z + behind.z);
		
		// figure out how much this particle's going to move
		
		float velocX, velocY, velocZ;
		
		if(randy.nextBoolean())
			velocX = randy.nextFloat() * velocityVariance.x;
		else
			velocX = randy.nextFloat() * -velocityVariance.x;
		
		if(randy.nextBoolean())
			velocY = randy.nextFloat() * velocityVariance.y;
		else
			velocY = randy.nextFloat() * -velocityVariance.y;
		
		if(randy.nextBoolean())
			velocZ = randy.nextFloat() * velocityVariance.z;
		else
			velocZ = randy.nextFloat() * -velocityVariance.z;
		
		Vector3f veloc = new Vector3f(velocX, velocY, velocZ);
		
		// rotate the velocity
		Vector3f rotVeloc = QuaternionHelper.rotateVectorByQuaternion(veloc, this.following.rotation);
		
		// get angular velocity and add it
		//javax.vecmath.Vector3f angvel = new javax.vecmath.Vector3f();
		//following.rigidBody.getAngularVelocity(angvel);
		//rotVeloc.set(rotVeloc.x + angvel.x, rotVeloc.y + angvel.y, rotVeloc.z + angvel.z);
		
		this.addParticle(new Particle(loc, 1.0f, 1.0f, randy.nextFloat() * particleTTLVariance, rotVeloc));
	}
} public class CollisionTypes {
	public static final short NOTHING = 0;
	public static final short SHIP = bit(0);
	public static final short WALL = bit(1);
	public static final short PLANET = bit(2);
	public static final short PROJECTILE = bit(3);
	public static final short PICKUP = bit(4);
	
	public static final short EVERYTHING = (short)(SHIP | WALL | PLANET | PROJECTILE | PICKUP); 
	
	private static short bit(int x) {
		return (short) (1 << x);
	}
} public class ConvexResultCallback<T extends DynamicEntity> extends CollisionWorld.ConvexResultCallback{
	/** list of hits */
	private ArrayList<T> hits;
	
	/**
	 * @param hits List to add hits to
	 * @param collisionMask What to look for
	 */
	public ConvexResultCallback(ArrayList<T> hits, short collisionMask){
		this.hits = hits;
		this.collisionFilterMask = collisionMask;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public float addSingleResult(LocalConvexResult convexResult, boolean normalInWorldSpace) {
		CollisionObject obj = convexResult.hitCollisionObject;
		
		DynamicEntity ent = (DynamicEntity) obj.getUserPointer();
		
		// dont' add the camera
		if(ent != Entities.camera)
			// might be doing some unsafe typecasting here, but I haven't run into any issues!
			hits.add((T)ent);
		
		return 0;
	}
	
} public class Physics {
	/** maximum number of substeps to do on each tick */
	private final static int SUBSTEPS = 10;

	/** the discrete dynamics world */
	public static DiscreteDynamicsWorld dynamicsWorld;

	/** broadphase interface */
	public static BroadphaseInterface broadphase;

	/** collision dispatcher */
	public static CollisionDispatcher dispatcher;

	/** impulse constraint solver */
	public static SequentialImpulseConstraintSolver solver;

	/** clock to use for framerate independence */
	private static Clock clock = new Clock();

	/** to keep the debug key from being held */
	private static boolean debugDown = false;

	/** whether or not to draw physics debug info */
	public static boolean drawDebug = false;

	/**
	 * Initializes the physics engine
	 */
	public static void initPhysics() {
		// broadphase interface
		broadphase = new DbvtBroadphase();

		// collision configuration and dispatcher
		DefaultCollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration();
		dispatcher = new CollisionDispatcher(collisionConfiguration);

		// wat
		solver = new SequentialImpulseConstraintSolver();

		// the world everything is in
		dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase,
				solver, collisionConfiguration);

		// no gravity, we're in space!
		dynamicsWorld.setGravity(new Vector3f(0, 0, 0));

		// see the PhysicsDebugDrawer class
		dynamicsWorld.setDebugDrawer(new PhysicsDebugDrawer());
		
		Physics.dynamicsWorld.setInternalTickCallback(new DynamicEntityCallback(), null);
	}

	/**
	 * Updates the dynamics world
	 */
	public static void update() {
		dynamicsWorld.stepSimulation(getDeltaTimeMicroseconds() / 1000000.0f,
				SUBSTEPS);

		// handle the physics debug key
		if (KeyBindings.SYS_DEBUG_PHYSICS.isPressed() && !debugDown) {
			drawDebug = !drawDebug;
			debugDown = true;
		}
		if (!KeyBindings.SYS_DEBUG_PHYSICS.isPressed()) {
			debugDown = false;
		}
	}

	/**
	 * Cleans up all the physics stuff
	 */
	public static void cleanup() {
		dynamicsWorld.destroy();
		dynamicsWorld = null;
		broadphase = null;
		dispatcher = null;
		solver = null;

	}

	/**
	 * @return Change in time in microseconds
	 */
	private static float getDeltaTimeMicroseconds() {
		float delta = clock.getTimeMicroseconds();
		clock.reset();
		return delta;
	}
	
	public static void convexSweepTest(DynamicEntity origin, org.lwjgl.util.vector.Vector3f distance, ConvexShape shape, CollisionWorld.ConvexResultCallback callback){
		// grab current world transform
		Transform from = new Transform(), to = new Transform();
		origin.rigidBody.getWorldTransform(to);
		origin.rigidBody.getWorldTransform(from);
		
		// set 'to' transform to be in front of the player
		org.lwjgl.util.vector.Vector3f forward = QuaternionHelper.rotateVectorByQuaternion(distance, origin.rotation);
		from.origin.add(new javax.vecmath.Vector3f(forward.x, forward.y, forward.z));
		
		dynamicsWorld.convexSweepTest(shape, to, from, callback);	
	}
} public class QuaternionHelper {
	final static float PIOVER180 = ((float) Math.PI) / 180.0f;

	/**
	 * Converts angles to a quaternion
	 * @param pitch X axis rotation
	 * @param yaw Y axis rotation
	 * @param roll Z axis rotation
	 * @return Quaternion representing angles
	 */
	public static Quaternion getQuaternionFromAngles(float pitch, float yaw,
			float roll) {
		Quaternion quat;

		float p = pitch * PIOVER180 / 2.0f;
		float y = yaw * PIOVER180 / 2.0f;
		float r = roll * PIOVER180 / 2.0f;

		float sinp = (float) Math.sin(p);
		float siny = (float) Math.sin(y);
		float sinr = (float) Math.sin(r);
		float cosp = (float) Math.cos(p);
		float cosy = (float) Math.cos(y);
		float cosr = (float) Math.cos(r);

		float quatX = sinr * cosp * cosy - cosr * sinp * siny;
		float quatY = cosr * sinp * cosy + sinr * cosp * siny;
		float quatZ = cosr * cosp * siny - sinr * sinp * cosy;
		float quatW = cosr * cosp * cosy + sinr * sinp * siny;

		quat = new Quaternion(quatX, quatY, quatZ, quatW);
		Quaternion retQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		Quaternion.normalise(quat, retQuat);

		return retQuat;
	}

	/**
	 * Not sure if this works
	 * @param quat Quaternion to convert
	 * @return float array with 4 elements- x, y, z, and angle
	 */
	public static float[] convertToAxisAngle(Quaternion quat) {
		float scale = (float) Math
				.sqrt(((quat.x * quat.x) + (quat.y * quat.y) + (quat.z * quat.z)));

		float ax = quat.x / scale;
		float ay = quat.y / scale;
		float az = quat.z / scale;

		float angle = 2.0f * (float) Math.acos((double) quat.w);

		return new float[] { ax, ay, az, angle };
	}

	/**
	 * Converts a quaternion to a rotation matrix stored in a FloatBuffer
	 * 
	 * @param quat
	 *            The quaternion to convert
	 * @param dest
	 *            The float buffer to put the rotation matrix into. MUST have a
	 *            capacity of 16 and be direct
	 */
	public static void toFloatBuffer(Quaternion quat, FloatBuffer dest) {
		if (!dest.isDirect()) {
			System.out
					.println("QuaternionHelper toFloatBuffer was passed an indirect FloatBuffer!");
		} else if (dest.capacity() != 16) {
			System.out
					.println("QuaternionHelper toFloatBuffer was passed a buffer of the incorrect size!");
		} else {
			dest.clear();

			float x = quat.x;
			float y = quat.y;
			float z = quat.z;
			float w = quat.w;

			float x2 = x * x;
			float y2 = y * y;
			float z2 = z * z;
			float xy = x * y;
			float xz = x * z;
			float yz = y * z;
			float wx = w * x;
			float wy = w * y;
			float wz = w * z;

			dest.put(1.0f - 2.0f * (y2 + z2));
			dest.put(2.0f * (xy - wz));
			dest.put(2.0f * (xz + wy));
			dest.put(0.0f);
			dest.put(2.0f * (xy + wz));
			dest.put(1.0f - 2.0f * (x2 + z2));
			dest.put(2.0f * (yz - wx));
			dest.put(0.0f);
			dest.put(2.0f * (xz - wy));
			dest.put(2.0f * (yz + wx));
			dest.put(1.0f - 2.0f * (x2 + y2));
			dest.put(0.0f);
			dest.put(0.0f);
			dest.put(0.0f);
			dest.put(0.0f);
			dest.put(1.0f);

			dest.rewind();
		}
	}

	/**
	 * Rotates a vector by a quaternion
	 * 
	 * @param vector
	 *            The vector to rotate
	 * @param quat
	 *            The quaternion to rotate the vector by
	 * @return Rotate vector
	 */
	public static Vector3f rotateVectorByQuaternion(Vector3f vector,
			Quaternion quat) {
		Quaternion vecQuat = new Quaternion(vector.x, vector.y, vector.z, 0.0f);

		Quaternion quatNegate = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		quat.negate(quatNegate);

		Quaternion resQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		Quaternion.mul(vecQuat, quatNegate, resQuat);
		Quaternion.mul(quat, resQuat, resQuat);

		return new Vector3f(resQuat.x, resQuat.y, resQuat.z);
	}
	
	/**
	 * Rotates a vector by a quaternion
	 * 
	 * @param vector
	 *            The vector to rotate
	 * @param quat
	 *            The quaternion to rotate the vector by
	 * @return Rotate vector
	 */
	public static Vector3f rotateVectorByQuaternion(javax.vecmath.Vector3f vector,
			Quaternion quat) {
		Quaternion vecQuat = new Quaternion(vector.x, vector.y, vector.z, 0.0f);

		Quaternion quatNegate = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		quat.negate(quatNegate);

		Quaternion resQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		Quaternion.mul(vecQuat, quatNegate, resQuat);
		Quaternion.mul(quat, resQuat, resQuat);

		return new Vector3f(resQuat.x, resQuat.y, resQuat.z);
	}
	
	/**
	 * Rotates a vector by a quaternion
	 * 
	 * @param vector
	 *            The vector to rotate
	 * @param quat
	 *            The quaternion to rotate the vector by
	 * @return Rotate vector
	 */
	public static Vector3f rotateVectorByQuaternion(javax.vecmath.Vector3f vector,
			Quat4f quat) {
		Quaternion vecQuat = new Quaternion(vector.x, vector.y, vector.z, 0.0f);

		Quat4f quatNegate = new Quat4f(0.0f, 0.0f, 0.0f, 1.0f);
		quat.negate(quatNegate);

		Quaternion resQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		Quaternion.mul(vecQuat, new Quaternion(quatNegate.x, quatNegate.y, quatNegate.z, quatNegate.w), resQuat);
		Quaternion.mul(new Quaternion(quat.x, quat.y, quat.z, quat.w), resQuat, resQuat);

		return new Vector3f(resQuat.x, resQuat.y, resQuat.z);
	}

	/**
	 * Converts a quaternion to good ol' euler angles
	 * 
	 * @param quat
	 *            The quaternion to convert
	 * @return A vector containing the three euler angles
	 */
	public static Vector3f getEulerAnglesFromQuaternion(Quaternion quat) {
		float xn = (2 * ((quat.x * quat.y) + (quat.z * quat.w)))
				/ (1 - (2 * ((quat.y * quat.y) + (quat.z * quat.z))));
		float x = (float) (Math.atan((double) xn));

		float yn = 2 * ((quat.x * quat.z) - (quat.w * quat.y));
		float y = (float) (Math.asin((double) yn));

		float zn = (2 * ((quat.x * quat.w) + (quat.y * quat.z)))
				/ (1 - (2 * ((quat.z * quat.z) + (quat.w * quat.w))));
		float z = (float) (Math.atan((double) zn));

		x = x * (180.0f / (float) Math.PI);
		y = y * (180.0f / (float) Math.PI);
		z = z * (180.0f / (float) Math.PI);

		return new Vector3f(x, y, z);
	}
	
	/**
	 * Rotate a quaternion by a vector
	 * @param quat Quaternion to rotate
	 * @param amount Amount to rotate quaternion by
	 * @return Rotated quaternion
	 */
	public static Quaternion rotate(Quaternion quat, Vector3f amount){
		Quaternion ret = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		
		ret = rotateX(quat, amount.x);
		ret = rotateY(ret, amount.y);
		ret = rotateZ(ret, amount.z);
		
		return ret;
	}

	/**
	 * Rotate a quaternion along it's x axis a certain amount
	 * 
	 * @param amount
	 *            Amount to rotate the quaternion
	 * @return Rotated quaternion
	 */
	public static Quaternion rotateX(Quaternion quat, float amount) {
		Quaternion ret = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		double radHalfAngle = Math.toRadians((double) amount) / 2.0;
		float sinVal = (float) Math.sin(radHalfAngle);
		float cosVal = (float) Math.cos(radHalfAngle);
		Quaternion rot = new Quaternion(sinVal, 0.0f, 0.0f, cosVal);
		Quaternion.mul(quat, rot, ret);
		return ret;
	}

	/**
	 * Rotate a quaternion along it's y axis a certain amount
	 * 
	 * @param amount
	 *            Amount to rotate the quaternion
	 * @return Rotated quaternion
	 */
	public static Quaternion rotateY(Quaternion quat, float amount) {
		Quaternion ret = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		double radHalfAngle = Math.toRadians((double) amount) / 2.0;
		float sinVal = (float) Math.sin(radHalfAngle);
		float cosVal = (float) Math.cos(radHalfAngle);
		Quaternion rot = new Quaternion(0.0f, sinVal, 0.0f, cosVal);
		Quaternion.mul(quat, rot, ret);
		return ret;
	}

	/**
	 * Rotate a quaternion along it's z axis a certain amount
	 * 
	 * @param amount
	 *            Amount to rotate the quaternion
	 * @return Rotated quaternion
	 */
	public static Quaternion rotateZ(Quaternion quat, float amount) {
		Quaternion ret = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		double radHalfAngle = Math.toRadians((double) amount) / 2.0;
		float sinVal = (float) Math.sin(radHalfAngle);
		float cosVal = (float) Math.cos(radHalfAngle);
		Quaternion rot = new Quaternion(0.0f, 0.0f, sinVal, cosVal);
		Quaternion.mul(quat, rot, ret);
		return ret;
	}

	/**
	 * Moves the given vector a certain amount along the rotation of the given
	 * quaternion
	 * 
	 * @param quat
	 *            Rotation to use for movement
	 * @param vec
	 *            Vector to add to
	 * @param amount
	 *            Amount to add to the vector
	 */
	public static Vector3f moveX(Quaternion quat, Vector3f vec, float amount) {
		Vector3f ret = new Vector3f(0.0f, 0.0f, 0.0f);
		Vector3f multi = rotateVectorByQuaternion(new Vector3f(amount, 0.0f,
				0.0f), quat);
		Vector3f.add(vec, multi, ret);
		return ret;
	}

	/**
	 * Moves the given vector a certain amount along the rotation of the given
	 * quaternion
	 * 
	 * @param quat
	 *            Rotation to use for movement
	 * @param vec
	 *            Vector to add to
	 * @param amount
	 *            Amount to add to the vector
	 */
	public static Vector3f moveY(Quaternion quat, Vector3f vec, float amount) {
		Vector3f ret = new Vector3f(0.0f, 0.0f, 0.0f);
		Vector3f multi = rotateVectorByQuaternion(new Vector3f(0.0f, amount,
				0.0f), quat);
		Vector3f.add(vec, multi, ret);
		return ret;
	}

	/**
	 * Moves the given vector a certain amount along the rotation of the given
	 * quaternion
	 * 
	 * @param quat
	 *            Rotation to use for movement
	 * @param vec
	 *            Vector to add to
	 * @param amount
	 *            Amount to add to the vector
	 */
	public static Vector3f moveZ(Quaternion quat, Vector3f vec, float amount) {
		Vector3f ret = new Vector3f(0.0f, 0.0f, 0.0f);
		Vector3f multi = rotateVectorByQuaternion(new Vector3f(0.0f, 0.0f,
				amount), quat);
		Vector3f.add(vec, multi, ret);
		return ret;
	}
	
	/**
	 * Converts a quaternion to a rotation matrix
	 * @param quat Quaternion to convert
	 * @return Rotation matrix representing given quaternion
	 */
	public static Matrix4f toMatrix(Quaternion quat){
		float x2 = quat.x * quat.x;
		float y2 = quat.y * quat.y;
		float z2 = quat.z * quat.z;
		float xy = quat.x * quat.y;
		float xz = quat.x * quat.z;
		float yz = quat.y * quat.z;
		float wx = quat.w * quat.x;
		float wy = quat.w * quat.y;
		float wz = quat.w * quat.z;
		
		Matrix4f ret = new Matrix4f();


		ret.m00 = (1.0f - 2.0f * (y2 + z2));
		ret.m10 = (2.0f * (xy - wz));
		ret.m20 = (2.0f * (xz + wy));
		ret.m30 = (0.0f);
		
		ret.m01 = (2.0f * (xy + wz));
		ret.m11 = (1.0f - 2.0f * (x2 + z2));
		ret.m21 = (2.0f * (yz - wx));
		ret.m31 = (0.0f);
		
		ret.m02 = (2.0f * (xz - wy));
		ret.m12 = (2.0f * (yz + wx));
		ret.m22 = (1.0f - 2.0f * (x2 + y2));
		ret.m32 = (0.0f);
		
		ret.m03 = (0.0f);
		ret.m13 = (0.0f);
		ret.m23 = (0.0f);
		ret.m33 = (1.0f);
		
		return ret;
	}
	
	/**
	 * Finds the quaternion between two vectors. Assumes that the vectors are NOT unit length.
	 * @param vec1 First vector
	 * @param vec2 Second vector
	 * @return Quaternion between vectors
	 */
	public static Quaternion quaternionBetweenVectors(Vector3f vec1, Vector3f vec2){
		Vector3f c = new Vector3f();
		Vector3f.cross(vec1, vec2, c);
		
		double v1squr = (double)vec1.lengthSquared();
		double v2squr = (double)vec2.lengthSquared();
		double angle = Math.sqrt(v1squr * v2squr) + (double)Vector3f.dot(vec1, vec2);
		
		Quaternion q = new Quaternion(c.x, c.y, c.z, (float) angle);
		q.normalise(q);
		return q;
	}
	
	/**
	 * Converts an axis and an angle to a quaternion
	 * @param axis Axis
	 * @param angle Angle
	 * @return Quaternion representing rotation
	 */
	public static Quaternion quaternionFromAxisAngle(Vector3f axis, double angle){
		if(Math.abs(angle) < 1e-6)
			return new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		
		double halfAngle = angle * 0.5;
		
		float s = (float)Math.sin(halfAngle);
		
		float x = axis.x * s;
		float y = axis.y * s;
		float z = axis.z * s;
		float w = (float)Math.cos(halfAngle);
		
		return new Quaternion(x, y, z, w);
	}
} public class Explosion extends Entity{
	/** Emitter for particles */
	Emitter emitter;
	
	/** Time to live (how long the explosion lasts) */
	float ttl;
	
	/** How long the explosion has been alive */
	float lived;
	
	// info for particle emitter
	private Vector3f locationVariance = new Vector3f(5.0f, 5.0f, 5.0f);
	private Vector3f velocityVariance = new Vector3f(10.0f, 10.0f, 10.0f);
	private float emitSpeed = 0.05f;
	private int particlesPerEmission = 20;
	private float paritcleTTLVariance = 2.5f;
	
	private SoundSource explosionSound;
	
	/**
	 * Boom!
	 * @param location Where this explosion originates
	 * @param rotation FIXME is this even necessary?
	 */
	public Explosion(Vector3f location, float ttl, float size){
		this.location = location;
		this.rotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
		lived = 0.0f;
		this.ttl = ttl;
		
		locationVariance = new Vector3f(size, size, size);
		velocityVariance = new Vector3f(size * 2, size * 2, size * 2);
		particlesPerEmission = (int)size * 4;
		paritcleTTLVariance = size / 2.0f;
		
		emitter = new Emitter(this,
				Textures.FIRE,
				// no offset
				new Vector3f(0.0f, 0.0f, 0.0f),
				locationVariance, 
				velocityVariance, 
				emitSpeed, 
				particlesPerEmission, 
				paritcleTTLVariance);
		emitter.active = true;
		
		explosionSound = new SoundSource(Sounds.SPLODE, false, location, new Vector3f(0.0f, 0.0f, 0.0f));
		explosionSound.playSound();
		explosionSound.removeFlag = true;
	}

	@Override
	public void update(float timeStep){
		emitter.update(timeStep);
		
		lived += timeStep;
		if(lived >= ttl){
			emitter.active = false;
			
			if(!emitter.hasParticles())
				Entities.removePassiveEntity(this);
		}
	}

	@Override
	public void draw() {
		// hooray for abstraction
		emitter.draw();
	}

	@Override
	public void cleanup(){
		
	}
} public interface Projectile {
	/**
	 * How much damage does this bullet do when it hits something?
	 * @return Amount of damage to do
	 */
	public int getDamage();
	
	/**
	 * Who owns this bullet? (Who can't it hurt?)
	 * @return Whoever fired the bullet
	 */
	public Entity getOwner();
} public enum Models {
	WING_X(Paths.SHIPS_PATH.path() + "wing_x", 0.5f, Textures.WING_X),
	LASERBULLET(Paths.MODEL_PATH.path() + "laserbullet", 3.5f, Textures.LASERBULLET),
	SAUCER(Paths.SHIPS_PATH.path() + "saucer", 1.0f, Textures.SAUCER),
	SKYBOX(Paths.MODEL_PATH.path() + "box", 100000.0f, Textures.SKYBOX),
	BOX(Paths.MODEL_PATH.path() + "box", 1.0f, Textures.DIAMOND),
	WESCOTT(Paths.SHIPS_PATH.path() + "wescott-8-beta", 1.0f, Textures.WESCOTT),
	DIAMOND(Paths.MODEL_PATH.path() + "diamond", 2.0f, Textures.DIAMOND),
	MISSILE(Paths.MODEL_PATH.path() + "missile", 1.0f, Textures.MISSILE),
	ASTEROID(Paths.MODEL_PATH.path() + "asteroid2", 1.0f, Textures.ASTEROID);
	
	/** the actual model object */
	private Model model;
	/** which file to load the model from */
	private String file;
	/** what scale the model is at */
	private float scale;
	/** which texture to use for the model */
	private Textures texture;
	
	private Models(String file, float scale, Textures texture){
		this.file = file;
		this.scale = scale;
		this.texture = texture;
	}
	
	public Model getModel(){
		if(!modelLoaded())
			initModel();
		return model; 
	}
	
	public boolean modelLoaded(){
		return model != null;
	}
	
	protected void initModel(){
		//System.out.println("initializing model " + this);
		if(!modelLoaded()){
			model = ModelLoader.loadObjFile(file, scale, texture);
		}
	}
} public enum Sounds {
	DIAMOND_PICKUP(Paths.SOUND_PATH.path() + "diamond-pickup.wav", 1.0f, 0.6f),
	SPLODE(Paths.SOUND_PATH.path() + "splosion.wav", 1.0f, 25.0f),
	THRUSTER(Paths.SOUND_PATH.path() + "thruster.wav", 1.0f, 50.0f),
	PEW(Paths.SOUND_PATH.path() + "pew.wav", 1.0f, 1.5f),
	HIT(Paths.SOUND_PATH.path() + "hit.wav", 1.0f, 10.0f),
	
	SELECT(Paths.SOUND_PATH.path() + "select.wav", 1.0f, 1.0f),
	BACK(Paths.SOUND_PATH.path() + "back.wav", 1.0f, 1.0f),
	FRIENDLY_ALERT(Paths.SOUND_PATH.path() + "friendly-alert.wav", 1.0f, 1.0f);
	
	/** File to load sound from */
	private String file;
	
	/** Buffer to use for OpenAL stuff */
	private int bufferHandle = -1;
	
	/** Pitch and gain for sound */
	public float pitch, gain;
	
	/**
	 * Creates a new sound and sends its data to OpenAL
	 * @param file Location of sound
	 * @param pitch Pitch to use for sound
	 * @param gain Gain to use for sound
	 */
	private Sounds(String file, float pitch, float gain){
		this.pitch = pitch;
		this.gain = gain;
		this.file = file;
	}
	
	/**
	 * @return Handle to use for sound
	 */
	public int getHandle(){
		// make sure sound is initialized and initialize it if it's not
		if(bufferHandle == -1)
			initSound();
		return bufferHandle;
	}
	
	/**
	 * Initializes a sound
	 * @param file Location of sound file
	 */
	protected void initSound(){
		// in case this gets called more than once
		if(bufferHandle == -1){
			try{
				// generate buffer handle
				IntBuffer buf = BufferUtils.createIntBuffer(1);
				AL10.alGenBuffers(buf);
				bufferHandle = buf.get(0);
				
				// using a FileInputStream here causes a null pointer exception!
				BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
				WaveData waveFile = WaveData.create(in);
				in.close();
				
				AL10.alBufferData(bufferHandle, waveFile.format, waveFile.data, waveFile.samplerate);
				waveFile.dispose();
			} catch(IOException e){
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Deletes sound from memory (NOTE: Probably shouldn't be called during runtime!)
	 */
	public void shutdown(){
		AL10.alDeleteBuffers(bufferHandle);
	}
} public enum Textures {
	// Environment
	SKYBOX("PNG", Paths.TEXTURE_PATH.path() + "skybox.png", true, GL11.GL_NEAREST),
	WHITE("BMP", Paths.TEXTURE_PATH.path() + "white.bmp", true, GL11.GL_NEAREST),
	CHECKERS("BMP", Paths.TEXTURE_PATH.path() + "checkers.bmp", true, GL11.GL_NEAREST),
	
	// Model textures
	WING_X("PNG", Paths.SHIPS_PATH.path() + "wing_x/wing_x.png", false, GL11.GL_NEAREST),
	LASERBULLET("PNG", Paths.MODEL_PATH.path() + "laserbullet/laserbullet.png", false, GL11.GL_NEAREST),
	SAUCER("PNG", Paths.SHIPS_PATH.path() + "saucer/saucer.png", false, GL11.GL_NEAREST),
	WESCOTT("PNG", Paths.SHIPS_PATH.path() + "wescott-8-beta/wescott-8-beta.png", false, GL11.GL_NEAREST),
	DIAMOND("PNG", Paths.MODEL_PATH.path() + "diamond/diamond.png", false, GL11.GL_NEAREST),
	MISSILE("PNG", Paths.MODEL_PATH.path() + "missile/missile.png", false, GL11.GL_NEAREST),
	ASTEROID("PNG", Paths.MODEL_PATH.path() + "asteroid/asteroid.png", false, GL11.GL_LINEAR),
	TRANSPARENT_CHECKERS("PNG", Paths.TEXTURE_PATH.path() + "transparent_checkers.png", false, GL11.GL_NEAREST),
	
	// Planets
	EARTH ("JPG", Paths.TEXTURE_PATH.path() + "planets/earth.jpg", true, GL11.GL_LINEAR),
	MERCURY("JPG", Paths.TEXTURE_PATH.path() + "planets/mercury.jpg", true, GL11.GL_LINEAR),
	VENUS("JPG", Paths.TEXTURE_PATH.path() + "planets/venus.jpg", true, GL11.GL_LINEAR),
	MARS("JPG", Paths.TEXTURE_PATH.path() + "planets/mars.jpg", true, GL11.GL_LINEAR),
	SUN("JPG", Paths.TEXTURE_PATH.path() + "planets/sun.jpg", true, GL11.GL_LINEAR),
	
	// Particles
	FIRE("PNG", Paths.PARTICLE_IMAGE_PATH.path() + "fire.png", false, GL11.GL_NEAREST),
	TRAIL("PNG", Paths.PARTICLE_IMAGE_PATH.path() + "trail.png", false, GL11.GL_NEAREST),
	
	// GUI
	CROSSHAIR("PNG", Paths.CROSSHAIR_PATH.path(), true, GL11.GL_NEAREST),
	TARGET("PNG", Paths.TARGET_PATH.path(), false, GL11.GL_NEAREST),
	MENU_BACKGROUND1("JPG", Paths.BACKGROUND_IMAGE_PATH.path() + "apod1.jpg", false, GL11.GL_NEAREST),
	MENU_BACKGROUND2("JPG", Paths.BACKGROUND_IMAGE_PATH.path() + "apod2.jpg", false, GL11.GL_NEAREST),
	MENU_SPACEOUT_TEXT("PNG", Paths.MENU_IMAGE_PATH.path() + "spaceout.png", false, GL11.GL_NEAREST),
	
	MENU_PICKER_ACTIVE("PNG", Paths.FILEPICKER_IMAGE_PATH.path() + "active.png", false, GL11.GL_NEAREST),
	MENU_PICKER_MOUSEOVER("PNG", Paths.FILEPICKER_IMAGE_PATH.path() + "mouseover.png", false, GL11.GL_NEAREST),
	MENU_PICKER_SELECTED("PNG", Paths.FILEPICKER_IMAGE_PATH.path() + "selected.png", false, GL11.GL_NEAREST),
	MENU_PICKER_PRESSED("PNG", Paths.FILEPICKER_IMAGE_PATH.path() + "active.png", false, GL11.GL_NEAREST),
	
	MENU_BUTTON_ACTIVE("PNG",Paths.MENUBUTTON_IMAGE_PATH.path()  + "active.png", false, GL11.GL_NEAREST),
	MENU_BUTTON_MOUSEOVER("PNG",Paths.MENUBUTTON_IMAGE_PATH.path()  + "mouseover.png", false, GL11.GL_NEAREST),
	MENU_BUTTON_PRESSED("PNG",Paths.MENUBUTTON_IMAGE_PATH.path()  + "pressed.png", false, GL11.GL_NEAREST),
	MENU_BUTTON_INACTIVE("PNG",Paths.MENUBUTTON_IMAGE_PATH.path()  + "inactive.png", false, GL11.GL_NEAREST),
	
	BUILDER_OPEN("PNG", Paths.BUILDER_IMAGE_PATH.path + "open.png", true, GL11.GL_NEAREST),
	BUILDER_GRABBED("PNG", Paths.BUILDER_IMAGE_PATH.path + "grabbed.png", true, GL11.GL_NEAREST);
	
	/** the texture for the texture object */
	private Texture texture;
	
	/** the format of the file and the actual file path */
	private String format, file;
	
	/** whether or not the image is flipped */
	private boolean flipped;
	
	/** the GL filter to use for the image */
	private int filter;
	private Textures(String format, String file,
			boolean flipped, int filter){
		this.format = format;
		this.file = file;
		this.flipped = flipped;
		this.filter = filter;
	}
	
	/**
	 * @return Texture object for this texture reference. If the texture isn't loaded yet, it will be (which can cause the game to stop for a second- try to load textures with initTexture before using them!)
	 */
	public Texture texture(){
		if(!textureLoaded())
			initTexture();
		return texture; 
	}
	
	/**
	 * Initializes the texture object for this texture reference
	 */
	protected void initTexture(){
		if(!textureLoaded()){
			try{
				FileInputStream fis = new FileInputStream(file);
				texture = TextureLoader.getTexture(format, fis, flipped, filter);
				fis.close();
			} catch(IOException e){
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * @return Whether or not the texture object for this texture object has been loaded
	 */
	public boolean textureLoaded(){
		return texture != null;
	}
}
import java.util.ArrayList;
import java.util.Random;
import javax.vecmath.Quat4f;
import org.lwjgl.util.vector.Quaternion;
import org.lwjgl.util.vector.Vector3f;
import com.bitwaffle.spaceguts.audio.SoundSource;
import com.bitwaffle.spaceguts.entities.DynamicEntity;
import com.bitwaffle.spaceguts.entities.Entities;
import com.bitwaffle.spaceguts.entities.Entity;
import com.bitwaffle.spaceguts.entities.particles.Emitter;
import com.bitwaffle.spaceguts.physics.CollisionTypes;
import com.bitwaffle.spaceguts.physics.ConvexResultCallback;
import com.bitwaffle.spaceguts.physics.Physics;
import com.bitwaffle.spaceguts.util.QuaternionHelper;
import com.bitwaffle.spaceout.entities.passive.particles.Explosion;
import com.bitwaffle.spaceout.interfaces.Projectile;
import com.bitwaffle.spaceout.resources.Models;
import com.bitwaffle.spaceout.resources.Sounds;
import com.bitwaffle.spaceout.resources.Textures;
import com.bulletphysics.collision.shapes.SphereShape;
import com.bulletphysics.linearmath.Transform;
package com.bitwaffle.spaceout.entities.dynamic;





/**
 * A homing missile
 * 
 * @author TranquilMarmot
 */
public class Missile extends DynamicEntity implements Projectile{
	/** How much damage a missile does when it hits something */
	private static final int DAMAGE = 50;
	
	/**  Missile's current speed */
	private float speed;
	
	/** How much faster the missile goes each update */
	private float speedIncrease;
	/** How long the missile has to live */
	private float detonationTime, timeLived;
	
	// info for particles
	private static final Vector3f FIRE_OFFSET = new Vector3f(0.0f, 0.0f, -2.0f);
	private static final Vector3f FIRE_LOC_VARIANCE = new Vector3f(1.0f, 1.0f, 1.0f);
	private static final Vector3f FIRE_VELOC_VARIANCE = new Vector3f(2.0f, 2.0f, 5.0f);
	private static final float FIRE_EMIT_SPEED = 0.05f;
	private static final int FIRE_PARTICLES_PER_EMISSION = 2;
	private static final float FIRE_PARTICLE_TTL_VARIANCE = 2.0f;
	
	private static Vector3f explosionDistance = new Vector3f(0.0f, 0.0f, 1.0f);
	private static float explosionSize = 150.0f;
	private static float explosionForce = 50.0f;
	
	/** What the missile is aiming for */
	private DynamicEntity target;
	
	/** Particle effect */
	private Emitter fire;
	
	/** The sound the missile makes */
	private SoundSource thrusterSound;

	/**
	 * Create a new missile
	 * @param location Where to put missile
	 * @param rotation Which way the missile should be facing
	 * @param target What the missile should try and hit (null if nothing)
	 * @param initialSpeed How fast the missile will be going when it's created
	 * @param speedIncrease How much faster the missile goes each update
	 * @param timeToDetonation How long the missile has to live
	 */
	public Missile(Vector3f location, Quaternion rotation, DynamicEntity target, float initialSpeed, float speedIncrease, float timeToDetonation) {
		// TODO missiles should probably be represented by cylinders instead of a convex hull
		super(location, rotation, Models.MISSILE, 100.0f, 1.0f, CollisionTypes.PROJECTILE,
				CollisionTypes.EVERYTHING);
		
		this.type = "Missile";
		
		this.target = target;
		this.speed = initialSpeed;
		this.speedIncrease = speedIncrease;
		this.detonationTime = timeToDetonation;
		this.timeLived = 0.0f;
		
		fire = new Emitter(this, Textures.FIRE, FIRE_OFFSET, FIRE_LOC_VARIANCE, FIRE_VELOC_VARIANCE, FIRE_EMIT_SPEED, FIRE_PARTICLES_PER_EMISSION, FIRE_PARTICLE_TTL_VARIANCE);
		
		// create looping sound and play it
		Vector3f veloc = QuaternionHelper.rotateVectorByQuaternion(new Vector3f(0.0f, 0.0f, initialSpeed), rotation);
		thrusterSound = new SoundSource(Sounds.THRUSTER, true, this.location, veloc);
		thrusterSound.playSound();
	}
	
	/**
	 * Ker-plow boooom
	 */
	public void explode(){
		// particle effect for explosion
		Explosion splode = new Explosion(this.location, 1.0f, 5.0f);
		Entities.addPassiveEntity(splode);