package com.bitwaffle.spaceout.entities.dynamic; 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; /** * 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); // stop and remove sound thrusterSound.stopSound(); thrusterSound.removeFlag = true; // perform a convex sweep test to push thing outwards ArrayList<DynamicEntity> hits = new ArrayList<DynamicEntity>(); ConvexResultCallback<DynamicEntity> callback = new ConvexResultCallback<DynamicEntity>(hits, CollisionTypes.EVERYTHING); Physics.convexSweepTest(this, explosionDistance, new SphereShape(explosionSize), callback); for(DynamicEntity ent : hits){ if(ent != this){ Vector3f subtract = new Vector3f(); Vector3f.sub(this.location, ent.location, subtract); subtract.negate(subtract); subtract.normalise(subtract); javax.vecmath.Vector3f entSpeed = new javax.vecmath.Vector3f(); ent.rigidBody.getLinearVelocity(entSpeed); float dx = entSpeed.x + (subtract.x * explosionForce); float dy = entSpeed.y + (subtract.y * explosionForce); float dz = entSpeed.z + (subtract.z * explosionForce); ent.rigidBody.setLinearVelocity(new javax.vecmath.Vector3f(dx, dy, dz)); } } // get rid of ze missile removeFlag = true; } @Override public void update(float timeStep){ /* * If there's no target, the missile is given a random spin, * so that it look's like it's going haywire. */ if(target == null){ javax.vecmath.Vector3f angvec = new javax.vecmath.Vector3f(); this.rigidBody.getAngularVelocity(angvec); if(angvec.length() == 0.0f){ Random r = new Random(); float x,y,z; if(r.nextBoolean()) x = r.nextFloat() * speed; else x = r.nextFloat() * -speed; if(r.nextBoolean()) y = r.nextFloat() * speed; else y = r.nextFloat() * -speed; if(r.nextBoolean()) z = r.nextFloat() * speed; else z = r.nextFloat() * -speed; angvec.set(x,y,z); angvec.normalize(angvec); this.rigidBody.setAngularVelocity(angvec); } // give the missile some forward momentum Vector3f forward = QuaternionHelper.rotateVectorByQuaternion(new Vector3f(0.0f, 0.0f, speed), this.rotation); this.rigidBody.setLinearVelocity(new javax.vecmath.Vector3f(forward.x, forward.y, forward.z)); /* * Else there is a target, and we should move towards it */ } else{ // make sure the target still exists if(target.removeFlag){ target = null; }else{ // find the difference between this's location and the target's location then negate it to go towards it Vector3f subtract = new Vector3f(); Vector3f.sub(this.location, target.location, subtract); subtract.negate(subtract); // since we normalize the difference, the length of the missile's linear velocity will be its speed! subtract.normalise(subtract); float dx1 = (subtract.x * speed); float dy1 = (subtract.y * speed); float dz1 = (subtract.z * speed); // set linear velocity to go towards following this.rigidBody.setLinearVelocity(new javax.vecmath.Vector3f(dx1, dy1, dz1)); if(dx1 != 0.0f || dy1 != 0.0f || dz1 != 0.0f){ //FIXME this doesn't work quite right... Quaternion newRot = QuaternionHelper.quaternionBetweenVectors(this.location, subtract); this.rotation.set(newRot); Transform trans = new Transform(); this.rigidBody.getWorldTransform(trans); trans.setRotation(new Quat4f(newRot.x, newRot.y, newRot.z, newRot.w)); this.rigidBody.setWorldTransform(trans); } } } // increase the missile's speed speed += speedIncrease * timeStep; // set sound location and velocity javax.vecmath.Vector3f veloc = new javax.vecmath.Vector3f(); this.rigidBody.getLinearVelocity(veloc); thrusterSound.setVelocity(new Vector3f(veloc.x, veloc.y, veloc.z)); thrusterSound.setLocation(this.location); // age the missile and explode if necessary timeLived += timeStep; if(timeLived >= detonationTime){ explode(); } // update particle trail fire.update(timeStep); } @Override public void draw(){ super.draw(); fire.draw(); } @Override public int getDamage() { return DAMAGE; } @Override public Entity getOwner() { return null; } }